@reformer/core 11.0.0-beta.8 → 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.
- package/README.md +91 -73
- package/dist/behaviors-value-HA_Q03hc.js +108 -0
- package/dist/behaviors.js +370 -242
- package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +28 -7
- package/dist/form/behaviors/context.d.ts +19 -2
- package/dist/form/behaviors/index.d.ts +3 -3
- package/dist/form/behaviors/internals.d.ts +9 -2
- package/dist/form/behaviors/node.d.ts +25 -2
- package/dist/form/behaviors/operators.d.ts +17 -9
- package/dist/form/behaviors/schema-operators.d.ts +6 -4
- package/dist/form/behaviors/types.d.ts +12 -4
- package/dist/form/create-form.d.ts +47 -35
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +45 -28
- package/dist/form/form-config-base.d.ts +46 -0
- package/dist/form/form-level.d.ts +121 -0
- package/dist/form/form-proxy-builder.d.ts +6 -22
- package/dist/form/index.d.ts +16 -21
- package/dist/form/nodes/form-array-node.d.ts +100 -0
- package/dist/form/nodes/form-control-node.d.ts +73 -0
- package/dist/form/nodes/form-group-node.d.ts +66 -0
- package/dist/form/nodes/node-kind.d.ts +29 -0
- package/dist/form/nodes/state/availability.d.ts +6 -0
- package/dist/form/nodes/state/component.d.ts +6 -0
- package/dist/form/nodes/state/index.d.ts +12 -0
- package/dist/form/nodes/state/interaction.d.ts +13 -0
- package/dist/form/nodes/state/issues.d.ts +9 -0
- package/dist/form/nodes/state/pending.d.ts +6 -0
- package/dist/form/schema/compile.d.ts +59 -0
- package/dist/form/schema/compiled.d.ts +143 -0
- package/dist/form/schema/row-key.d.ts +11 -0
- package/dist/form/schema-controller.d.ts +30 -14
- package/dist/form/signal-node-registry.d.ts +6 -7
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +19 -92
- package/dist/form/types/field-config.d.ts +27 -0
- package/dist/form/types/index.d.ts +5 -57
- package/dist/form/types/nodes.d.ts +159 -0
- package/dist/form/types/schema-node.d.ts +139 -63
- package/dist/form/types/validation-schema.d.ts +23 -27
- package/dist/form/validation/context.d.ts +42 -7
- package/dist/form/validation/index.d.ts +9 -8
- package/dist/form/validation/operators.d.ts +22 -30
- package/dist/form/validation/run.d.ts +93 -18
- package/dist/form/validation/strategy.d.ts +41 -13
- package/dist/form/validation/types.d.ts +74 -20
- package/dist/form/validators/date-utils.d.ts +13 -1
- package/dist/form/validators/email.d.ts +8 -7
- package/dist/form/validators/file-type.d.ts +8 -7
- package/dist/form/validators/file-utils.d.ts +2 -1
- package/dist/form/validators/future-date.d.ts +8 -7
- package/dist/form/validators/index.d.ts +1 -1
- package/dist/form/validators/integer.d.ts +8 -7
- package/dist/form/validators/is-date.d.ts +11 -9
- package/dist/form/validators/is-number.d.ts +11 -9
- package/dist/form/validators/max-age.d.ts +13 -9
- package/dist/form/validators/max-date.d.ts +8 -7
- package/dist/form/validators/max-file-size.d.ts +8 -7
- package/dist/form/validators/max-files.d.ts +8 -7
- package/dist/form/validators/max-length.d.ts +14 -7
- package/dist/form/validators/max-total-file-size.d.ts +8 -7
- package/dist/form/validators/max.d.ts +10 -8
- package/dist/form/validators/min-age.d.ts +12 -9
- package/dist/form/validators/min-date.d.ts +8 -7
- package/dist/form/validators/min-file-size.d.ts +8 -7
- package/dist/form/validators/min-files.d.ts +11 -9
- package/dist/form/validators/min-length.d.ts +14 -7
- package/dist/form/validators/min.d.ts +10 -8
- package/dist/form/validators/multiple-of.d.ts +10 -8
- package/dist/form/validators/non-negative.d.ts +8 -7
- package/dist/form/validators/non-zero.d.ts +8 -7
- package/dist/form/validators/past-date.d.ts +8 -7
- package/dist/form/validators/pattern.d.ts +9 -8
- package/dist/form/validators/phone.d.ts +8 -7
- package/dist/form/validators/required.d.ts +11 -9
- package/dist/form/validators/url.d.ts +20 -13
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +1 -1
- package/dist/i18n/en.json.d.ts +5 -1
- package/dist/i18n/locale-store.d.ts +7 -0
- package/dist/i18n/message-format.d.ts +24 -4
- package/dist/i18n/ru.json.d.ts +5 -1
- package/dist/i18n/translator.d.ts +11 -0
- package/dist/i18n/validation-message.d.ts +42 -7
- package/dist/i18n.d.ts +7 -12
- package/dist/i18n.js +66 -283
- package/dist/index.d.ts +16 -7
- package/dist/index.js +924 -2855
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.js +1 -1
- package/dist/locale/en.json +4 -1
- package/dist/locale/ru.js +4 -1
- package/dist/locale/ru.json +4 -1
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.js +1 -1
- package/dist/model/behaviors-value.d.ts +24 -6
- package/dist/model/create-model.d.ts +2 -2
- package/dist/model/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +11 -5
- package/dist/model/model-nodes.d.ts +79 -12
- package/dist/model/model-signals-proxy.d.ts +5 -6
- package/dist/model/model-value-proxy.d.ts +20 -15
- package/dist/model/safe-effect.d.ts +33 -1
- package/dist/model/types.d.ts +63 -13
- package/dist/model.js +20 -18
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
- package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
- package/dist/platforms/react/form-bundle-context.d.ts +2 -2
- package/dist/platforms/react/hooks/types.d.ts +57 -38
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
- package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
- package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
- package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
- package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +6 -1
- package/dist/platforms/react/i18n/hooks.d.ts +16 -10
- package/dist/platforms/react/i18n/provider.d.ts +9 -2
- package/dist/platforms/react/index.d.ts +6 -4
- package/dist/react.d.ts +22 -0
- package/dist/react.js +505 -0
- package/dist/runtime-token.d.ts +1 -2
- package/dist/safe-effect-BbtX3KuN.js +643 -0
- package/dist/schema-controller-CpiO2JoK.js +143 -0
- package/dist/signals.d.ts +15 -9
- package/dist/signals.js +11 -6
- package/dist/strategy-CoFSemuP.js +375 -0
- package/dist/type-guards-D-N_5Ddh.js +19 -0
- package/dist/validation-issue-B9mjI8jz.js +7 -0
- package/dist/validation-message-Ced9MGxs.js +194 -0
- package/dist/validation.js +9 -9
- package/dist/validators/email.js +5 -5
- package/dist/validators/future-date.js +8 -8
- package/dist/validators/integer.js +3 -3
- package/dist/validators/is-date.js +4 -4
- package/dist/validators/is-number.js +4 -4
- package/dist/validators/max-age.js +8 -9
- package/dist/validators/max-date.js +8 -8
- package/dist/validators/max-length.js +7 -7
- package/dist/validators/max.js +4 -4
- package/dist/validators/min-age.js +10 -8
- package/dist/validators/min-date.js +3 -3
- package/dist/validators/min-length.js +5 -5
- package/dist/validators/min.js +4 -4
- package/dist/validators/multiple-of.js +11 -7
- package/dist/validators/non-negative.js +3 -3
- package/dist/validators/non-zero.js +3 -3
- package/dist/validators/past-date.js +8 -8
- package/dist/validators/pattern.js +5 -5
- package/dist/validators/phone.js +11 -9
- package/dist/validators/required.js +2 -2
- package/dist/validators/url.js +48 -12
- package/dist/validators.js +17 -17
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +111 -80
- package/docs/llms/03-api-signatures.md +368 -153
- package/docs/llms/04-common-patterns.md +58 -56
- package/docs/llms/05-common-mistakes.md +69 -44
- package/docs/llms/06-troubleshooting.md +14 -9
- package/docs/llms/07-complete-import.md +74 -43
- package/docs/llms/08-form-types.md +11 -4
- package/docs/llms/09-formschema.md +129 -81
- package/docs/llms/10-arrays.md +106 -75
- package/docs/llms/11-async-watchfield.md +2 -2
- package/docs/llms/13-multi-step.md +134 -63
- package/docs/llms/14-extended-mistakes.md +20 -17
- package/docs/llms/15-project-structure.md +221 -74
- package/docs/llms/16-ui-components.md +55 -42
- package/docs/llms/17-nonexistent-api.md +199 -32
- package/docs/llms/18-conditional-fields.md +66 -32
- package/docs/llms/19-reading-values.md +15 -3
- package/docs/llms/20-compute-vs-watch.md +16 -10
- package/docs/llms/21-array-operations.md +51 -27
- package/docs/llms/22-cycle-detection.md +1 -1
- package/docs/llms/23-copy-from.md +2 -2
- package/docs/llms/24-sync-fields.md +6 -5
- package/docs/llms/25-reset-when.md +14 -7
- package/docs/llms/26-transform-value.md +4 -4
- package/docs/llms/27-revalidate-when.md +16 -15
- package/docs/llms/28-submit-and-reset.md +276 -128
- package/docs/llms/29-async-preload.md +39 -29
- package/docs/llms/30-type-safety-recipes.md +105 -51
- package/docs/llms/31-async-validator-debounce.md +96 -53
- package/docs/llms/33-validation-strategy.md +177 -65
- package/docs/llms/34-i18n.md +69 -43
- package/docs/llms/35-unified-contract.md +100 -39
- package/docs/llms/36-issue-messages.md +284 -0
- package/docs/llms/37-compiled-schema.md +332 -0
- package/docs/llms/38-form-flow.md +397 -0
- package/llms-index.json +1 -1
- package/llms.txt +6084 -3420
- package/package.json +23 -4
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/form/create-core-form.d.ts +0 -72
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -189
- package/dist/form/form-submitter.d.ts +0 -121
- package/dist/form/nodes/array-node.d.ts +0 -276
- package/dist/form/nodes/field-node.d.ts +0 -297
- package/dist/form/nodes/form-node.d.ts +0 -326
- package/dist/form/nodes/group-node.d.ts +0 -284
- package/dist/form/nodes/model-array-node.d.ts +0 -126
- package/dist/form/nodes/subscription-manager.d.ts +0 -180
- package/dist/form/status-machine.d.ts +0 -115
- package/dist/form/types/deep-schema.d.ts +0 -107
- package/dist/form/types/form-proxy.d.ts +0 -141
- package/dist/form/unique-id.d.ts +0 -45
- package/dist/form/validation/config.d.ts +0 -73
- package/dist/form/validation/error-handler.d.ts +0 -166
- package/dist/form/validators/validation-error.d.ts +0 -8
- package/dist/index-BrNkQj1y.js +0 -105
- package/dist/loader-Dv_Hl7JC.js +0 -216
- package/dist/model-value-proxy-caTmAiUd.js +0 -419
- package/dist/node-BeV_lV5i.js +0 -100
- package/dist/strategy-DgzELWno.js +0 -204
- package/dist/validation-error-B-U3RsV0.js +0 -11
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { FormNode } from './nodes
|
|
1
|
+
import { FormNode } from './types/nodes';
|
|
2
2
|
/**
|
|
3
3
|
* Связать ручку модели с её нодой формы.
|
|
4
4
|
*
|
|
5
5
|
* Вызывается движком при сборке формы (`createForm`/{@link createFormFromModel}) для каждого поля,
|
|
6
|
-
* группы и массива
|
|
6
|
+
* группы и массива под-форм; у группы в реестре лежит её лицо {@link FormGroup} с нодами полей.
|
|
7
|
+
* Прикладной код обычно этот реестр не заполняет напрямую.
|
|
7
8
|
*
|
|
8
9
|
* @param signal - Ручка из {@link FormModel}: сигнал листа, узел-массив или узел-группа дерева `model.$`
|
|
9
10
|
* @param node - Нода формы, отвечающая за эту часть модели
|
|
10
11
|
*
|
|
11
|
-
* @example
|
|
12
|
+
* @example Ручка модели указывает на ноду другой сборки
|
|
12
13
|
* ```typescript
|
|
13
14
|
* import { registerSignalNode } from '@reformer/core';
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
* const node = group.getFieldByPath('profile.email');
|
|
17
|
-
* if (sig && node) registerSignalNode(sig, node);
|
|
16
|
+
* registerSignalNode(model.$.profile.email, profileForm.email);
|
|
18
17
|
* ```
|
|
19
18
|
*
|
|
20
19
|
* @see {@link getNodeForSignal} - обратный поиск ноды по ручке
|
|
@@ -37,7 +36,7 @@ export declare function registerSignalNode(signal: object, node: FormNode<any>):
|
|
|
37
36
|
* import { getNodeForSignal } from '@reformer/core';
|
|
38
37
|
*
|
|
39
38
|
* const sig = model.$.email;
|
|
40
|
-
* getNodeForSignal(sig)?.
|
|
39
|
+
* getNodeForSignal(sig)?.setIssues([{ code: 'required' }]);
|
|
41
40
|
* ```
|
|
42
41
|
*
|
|
43
42
|
* @example Нода группы и строки массива
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { ValidationResult } from './validation/types';
|
|
3
|
+
/**
|
|
4
|
+
* Итог отправки: `submitted` — обработчик отработал, `invalid` / `error` / `cancelled` —
|
|
5
|
+
* проверка не пропустила (обработчик не вызывался), `failed` — обработчик бросил исключение.
|
|
6
|
+
*
|
|
7
|
+
* `cancelled` — проверку отправки сменила следующая (вторая отправка, живой прогон) либо модель
|
|
8
|
+
* изменилась, пока проверка шла: правила видели прежние значения, и отправлять новые нельзя.
|
|
9
|
+
* Замечаний такой итог не несёт — отправку повторяют.
|
|
10
|
+
*
|
|
11
|
+
* @group Form
|
|
12
|
+
*/
|
|
13
|
+
export type SubmitResult<TResult> = {
|
|
14
|
+
readonly status: 'submitted';
|
|
15
|
+
readonly result: TResult;
|
|
16
|
+
} | {
|
|
17
|
+
readonly status: 'invalid' | 'error' | 'cancelled';
|
|
18
|
+
readonly validation: ValidationResult;
|
|
19
|
+
} | {
|
|
20
|
+
readonly status: 'failed';
|
|
21
|
+
readonly error: unknown;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Итог проверки для действия по её результату (отправка, переход шага).
|
|
25
|
+
*
|
|
26
|
+
* @group Form
|
|
27
|
+
*/
|
|
28
|
+
export interface ActionCheck<T> {
|
|
29
|
+
/** Результат проверки. */
|
|
30
|
+
readonly result: ValidationResult;
|
|
31
|
+
/** Значения модели после проверки; при `stale === false` — ровно те, что видели правила. */
|
|
32
|
+
readonly values: T;
|
|
33
|
+
/** Проверка успешна, но модель с тех пор изменилась: её успех относится к прежним значениям. */
|
|
34
|
+
readonly stale: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Зависимости отправки. Явные, чтобы модуль не тянул ни модель, ни ноды формы.
|
|
38
|
+
*
|
|
39
|
+
* @group Form
|
|
40
|
+
*/
|
|
41
|
+
export interface SubmissionDependencies<T> {
|
|
42
|
+
/**
|
|
43
|
+
* Проверка перед отправкой — попытка отправки: замечания раскрыты, при необходимости вся форма
|
|
44
|
+
* тронута. `undefined` — правил нет: обработчик получает {@link SubmissionDependencies.values}.
|
|
45
|
+
*/
|
|
46
|
+
readonly check: (() => Promise<ActionCheck<T>>) | undefined;
|
|
47
|
+
/** Значения без проверки. */
|
|
48
|
+
readonly values: () => T;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Отправка формы.
|
|
52
|
+
*
|
|
53
|
+
* @group Form
|
|
54
|
+
*/
|
|
55
|
+
export interface FormSubmission<T> {
|
|
56
|
+
/** Идёт ли отправка: работает обработчик. Пока идёт проверка, — `false`. */
|
|
57
|
+
readonly submitting: ReadonlySignal<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Отправить: один прогон всех правил; при успехе — обработчик со значениями модели.
|
|
60
|
+
*
|
|
61
|
+
* Обработчик получает ровно те значения, которые видели правила. Пока идёт async-правило,
|
|
62
|
+
* форма не заперта (`submitting` ещё `false`); если модель за это время изменилась — правкой
|
|
63
|
+
* пользователя, загрузкой, записью поведения после таймера или запроса, — итог `cancelled`,
|
|
64
|
+
* обработчик не вызывается.
|
|
65
|
+
*
|
|
66
|
+
* Значения — модель целиком, с выключенными полями: их правила не считаются, но данные
|
|
67
|
+
* принадлежат модели. Убрать их из отправки — забота обработчика либо
|
|
68
|
+
* `enableWhen(…, { resetOnDisable: true })`.
|
|
69
|
+
*
|
|
70
|
+
* Исключение обработчика наружу не выходит: итог `failed` с этим исключением.
|
|
71
|
+
*/
|
|
72
|
+
submit<TResult>(handler: (values: T) => TResult | Promise<TResult>): Promise<SubmitResult<TResult>>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Создать отправку формы.
|
|
76
|
+
*
|
|
77
|
+
* Обычно её создаёт сборка (`createForm` → `bundle.submission`). Прямой вызов нужен там, где
|
|
78
|
+
* сборки нет: визард в разметке без `createForm` отправляет значения формы без проверки.
|
|
79
|
+
*
|
|
80
|
+
* @typeParam T - Форма данных.
|
|
81
|
+
* @param dependencies - {@link SubmissionDependencies}: проверка перед действием и значения.
|
|
82
|
+
* @returns {@link FormSubmission}.
|
|
83
|
+
*
|
|
84
|
+
* @example Без проверки — значения формы как есть
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const submission = createSubmission<Profile>({ check: undefined, values: () => form.getValue() });
|
|
87
|
+
* const outcome = await submission.submit(api.save);
|
|
88
|
+
* if (outcome.status === 'failed') showToast('Не удалось сохранить');
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @group Form
|
|
92
|
+
*/
|
|
93
|
+
export declare function createSubmission<T>(dependencies: SubmissionDependencies<T>): FormSubmission<T>;
|
|
@@ -1,97 +1,38 @@
|
|
|
1
|
-
import { FormNode } from './nodes
|
|
2
|
-
import { FieldNode } from './nodes/field-node';
|
|
3
|
-
import { GroupNode } from './nodes/group-node';
|
|
4
|
-
import { ArrayNode } from './nodes/array-node';
|
|
5
|
-
import { FormValue } from './types/index';
|
|
1
|
+
import { FormArray, FormControl, FormGroup, FormNode } from './types/nodes';
|
|
6
2
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Проверяет базовые свойства, общие для всех типов узлов
|
|
10
|
-
*
|
|
11
|
-
* @group Utilities
|
|
12
|
-
* @category Type Guards
|
|
13
|
-
*
|
|
14
|
-
* @param value - Значение для проверки
|
|
15
|
-
* @returns true если value является FormNode
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* if (isFormNode(value)) {
|
|
20
|
-
* value.setValue(newValue);
|
|
21
|
-
* value.validate();
|
|
22
|
-
* }
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export declare function isFormNode(value: unknown): value is FormNode<FormValue>;
|
|
26
|
-
/**
|
|
27
|
-
* Проверить, является ли значение FieldNode (примитивное поле)
|
|
28
|
-
*
|
|
29
|
-
* FieldNode представляет примитивное поле формы (string, number, boolean и т.д.)
|
|
30
|
-
* и имеет валидаторы, но не имеет вложенных полей или элементов массива
|
|
31
|
-
*
|
|
32
|
-
* @group Utilities
|
|
33
|
-
* @category Type Guards
|
|
34
|
-
*
|
|
35
|
-
* @param value - Значение для проверки
|
|
36
|
-
* @returns true если value является FieldNode
|
|
3
|
+
* Нода формы любого вида — поле, группа или массив под-форм.
|
|
37
4
|
*
|
|
38
5
|
* @example
|
|
39
6
|
* ```typescript
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* node.asyncValidators; // OK
|
|
43
|
-
* node.markAsTouched(); // OK
|
|
44
|
-
* }
|
|
7
|
+
* const node = getNodeForSignal(model.$.email);
|
|
8
|
+
* if (isFormNode(node)) node.markAsTouched();
|
|
45
9
|
* ```
|
|
46
10
|
*/
|
|
47
|
-
export declare function
|
|
11
|
+
export declare function isFormNode(value: unknown): value is FormNode<unknown>;
|
|
48
12
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* GroupNode представляет объект с вложенными полями формы: имеет навигацию по полям
|
|
52
|
-
* (`getFieldByPath`/`fields`) и НЕ имеет array-методов (`items`/`push`/`removeAt`).
|
|
53
|
-
*
|
|
54
|
-
* @param value - Значение для проверки
|
|
55
|
-
* @returns true если value является GroupNode
|
|
13
|
+
* Поле формы — нода над листом модели с пропсами компонента.
|
|
56
14
|
*
|
|
57
15
|
* @example
|
|
58
16
|
* ```typescript
|
|
59
|
-
* if (
|
|
60
|
-
* node.getFieldByPath('user.email'); // OK
|
|
61
|
-
* }
|
|
17
|
+
* if (isFormControl(node)) node.updateComponentProps({ placeholder: 'Email' });
|
|
62
18
|
* ```
|
|
63
19
|
*/
|
|
64
|
-
export declare function
|
|
20
|
+
export declare function isFormControl(value: unknown): value is FormControl<unknown>;
|
|
65
21
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* ArrayNode представляет массив вложенных форм (обычно GroupNode)
|
|
69
|
-
* и имеет array-like методы (push, removeAt, at)
|
|
70
|
-
*
|
|
71
|
-
* @param value - Значение для проверки
|
|
72
|
-
* @returns true если value является ArrayNode
|
|
22
|
+
* Группа полей — её прокси с нодами полей по именам.
|
|
73
23
|
*
|
|
74
24
|
* @example
|
|
75
25
|
* ```typescript
|
|
76
|
-
* if (
|
|
77
|
-
* node.push(); // OK - добавить элемент
|
|
78
|
-
* node.removeAt(0); // OK - удалить элемент
|
|
79
|
-
* const item = node.at(0); // OK - получить элемент
|
|
80
|
-
* }
|
|
26
|
+
* if (isFormGroup(node)) node.markAsTouched();
|
|
81
27
|
* ```
|
|
82
28
|
*/
|
|
83
|
-
export declare function
|
|
29
|
+
export declare function isFormGroup(value: unknown): value is FormGroup<Record<string, unknown>>;
|
|
84
30
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* Полезно для логирования и отладки
|
|
88
|
-
*
|
|
89
|
-
* @param node - Узел для проверки
|
|
90
|
-
* @returns Строковое название типа узла
|
|
31
|
+
* Массив под-форм.
|
|
91
32
|
*
|
|
92
33
|
* @example
|
|
93
34
|
* ```typescript
|
|
94
|
-
*
|
|
35
|
+
* if (isFormArray(node)) node.push();
|
|
95
36
|
* ```
|
|
96
37
|
*/
|
|
97
|
-
export declare function
|
|
38
|
+
export declare function isFormArray(value: unknown): value is FormArray<object>;
|
|
@@ -1,109 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Контракт замечания валидации.
|
|
3
3
|
*
|
|
4
|
-
* Базовый
|
|
5
|
-
*
|
|
6
|
-
* и платформенные биндинги. Файл не импортирует ничего — это лист графа зависимостей.
|
|
4
|
+
* Базовый словарь, на который опираются ноды формы, слой валидации и платформенные биндинги.
|
|
5
|
+
* Файл не импортирует ничего — это лист графа зависимостей.
|
|
7
6
|
*
|
|
8
|
-
* Живёт в `form/types/`, а не в слое модели:
|
|
9
|
-
*
|
|
7
|
+
* Живёт в `form/types/`, а не в слое модели: замечание — понятие формы, а модель этих типов
|
|
8
|
+
* не использует (и импортировать их оттуда запрещено границей model⇏form).
|
|
10
9
|
* Наружу отдаётся через `form/types/index`.
|
|
11
10
|
*
|
|
12
11
|
* @group Types
|
|
13
12
|
* @module form/types/contracts
|
|
14
13
|
*/
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* @category Core Types
|
|
21
|
-
*/
|
|
22
|
-
export type FormValue = string | number | boolean | null | undefined | Date | File | FormValue[] | {
|
|
23
|
-
[key: string]: FormValue;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Синхронная функция валидации
|
|
27
|
-
* @group Types
|
|
28
|
-
* @category Validation Types
|
|
29
|
-
*/
|
|
30
|
-
export type ValidatorFn<T = FormValue> = (value: T) => ValidationError | null;
|
|
31
|
-
/**
|
|
32
|
-
* Опции для асинхронного валидатора
|
|
33
|
-
* @group Types
|
|
34
|
-
* @category Validation Types
|
|
35
|
-
*/
|
|
36
|
-
export interface AsyncValidatorOptions {
|
|
37
|
-
/**
|
|
38
|
-
* AbortSignal для отмены валидации
|
|
39
|
-
* Позволяет отменить асинхронную операцию при новой валидации
|
|
40
|
-
*/
|
|
41
|
-
signal?: AbortSignal;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Асинхронная функция валидации
|
|
45
|
-
*
|
|
46
|
-
* @param value - Значение для валидации
|
|
47
|
-
* @param options - Опции валидации (опционально)
|
|
48
|
-
* @returns Promise с ошибкой валидации или null если значение валидно
|
|
15
|
+
* Замечание валидации: что нарушено (`code`) и с какими числами (`params`). Текста у замечания
|
|
16
|
+
* нет — отображаемую строку по коду даёт слой представления (`resolveIssueMessage` из
|
|
17
|
+
* `@reformer/core/i18n`): словарь формы → словарь локали → встроенный английский → сам код.
|
|
18
|
+
* Поэтому одно и то же замечание показывается на любом языке без повторной проверки.
|
|
49
19
|
*
|
|
50
20
|
* @example
|
|
51
|
-
* ```
|
|
52
|
-
*
|
|
53
|
-
* const
|
|
54
|
-
* const exists = await checkEmail(value);
|
|
55
|
-
* return exists ? { code: 'exists', message: 'Email already exists' } : null;
|
|
56
|
-
* };
|
|
57
|
-
*
|
|
58
|
-
* // Валидатор с поддержкой отмены
|
|
59
|
-
* const emailExistsAbortable: AsyncValidatorFn<string> = async (value, options) => {
|
|
60
|
-
* const exists = await fetch(`/api/check-email?email=${value}`, {
|
|
61
|
-
* signal: options?.signal // Передаём signal в fetch для отмены запроса
|
|
62
|
-
* });
|
|
63
|
-
* return exists ? { code: 'exists', message: 'Email already exists' } : null;
|
|
64
|
-
* };
|
|
21
|
+
* ```ts
|
|
22
|
+
* const tooShort: ValidationIssue = { code: 'minLength', params: { minLength: 8 } };
|
|
23
|
+
* const softLimit: ValidationIssue = { code: 'loanAmount.high', severity: 'warning' };
|
|
65
24
|
* ```
|
|
66
25
|
*
|
|
67
26
|
* @group Types
|
|
68
27
|
* @category Validation Types
|
|
69
28
|
*/
|
|
70
|
-
export
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*/
|
|
76
|
-
|
|
77
|
-
code: string;
|
|
78
|
-
/**
|
|
79
|
-
* Готовый текст ошибки. Пустая строка — «автор правила текста не задавал»: отображаемую строку
|
|
80
|
-
* тогда даёт резолвер по `code` (словарь локали `validation.<code>`).
|
|
81
|
-
*/
|
|
82
|
-
message: string;
|
|
83
|
-
/** Ключ сообщения в словаре приложения; при наличии в активной локали важнее `message`. */
|
|
84
|
-
messageKey?: string;
|
|
85
|
-
params?: Record<string, FormValue>;
|
|
86
|
-
/** Severity level: 'error' (default) blocks submission, 'warning' shows message but allows submission */
|
|
87
|
-
severity?: 'error' | 'warning';
|
|
29
|
+
export interface ValidationIssue {
|
|
30
|
+
/** Код правила: встроенный (`required`, `minLength`) либо свой (`loanAmount.required`). */
|
|
31
|
+
readonly code: string;
|
|
32
|
+
/** Значения для подстановки в текст: порог правила, фактическое значение. */
|
|
33
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
34
|
+
/** `'error'` (по умолчанию) блокирует отправку, `'warning'` показывается, но не блокирует. */
|
|
35
|
+
readonly severity?: 'error' | 'warning';
|
|
88
36
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Опции для фильтрации ошибок в методе getErrors()
|
|
91
|
-
* @group Types
|
|
92
|
-
* @category Validation Types
|
|
93
|
-
*/
|
|
94
|
-
export interface ErrorFilterOptions {
|
|
95
|
-
/** Фильтр по коду ошибки */
|
|
96
|
-
code?: string | string[];
|
|
97
|
-
/** Фильтр по сообщению (поддерживает частичное совпадение) */
|
|
98
|
-
message?: string;
|
|
99
|
-
/** Фильтр по параметрам ошибки */
|
|
100
|
-
params?: Record<string, FormValue>;
|
|
101
|
-
/** Кастомный предикат для фильтрации */
|
|
102
|
-
predicate?: (error: ValidationError) => boolean;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Статус поля формы
|
|
106
|
-
* @group Types
|
|
107
|
-
* @category Core Types
|
|
108
|
-
*/
|
|
109
|
-
export type FieldStatus = 'valid' | 'invalid' | 'pending' | 'disabled';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Signal } from '@preact/signals-core';
|
|
2
|
+
import { ComponentRef } from './schema-node';
|
|
3
|
+
/**
|
|
4
|
+
* Конфигурация поля формы ({@link FormControl}): сигнал значения из модели и то, что нода берёт
|
|
5
|
+
* из узла схемы.
|
|
6
|
+
*
|
|
7
|
+
* В схеме формы поле записывают узлом `{ model: model.$.field, component, componentProps }`;
|
|
8
|
+
* сборка `createFormFromModel` превращает узел в этот конфиг сама. Правил валидации в конфиге нет —
|
|
9
|
+
* они живут в схеме валидации (`defineValidation`, `@reformer/core/validation`).
|
|
10
|
+
*
|
|
11
|
+
* @group Types
|
|
12
|
+
* @category Configuration Types
|
|
13
|
+
*/
|
|
14
|
+
export interface FieldConfig<T> {
|
|
15
|
+
/**
|
|
16
|
+
* Сигнал значения из {@link FormModel} — источник истины значения поля. Нода значением не
|
|
17
|
+
* владеет, а ссылается на этот сигнал.
|
|
18
|
+
*/
|
|
19
|
+
valueSignal: Signal<T>;
|
|
20
|
+
/**
|
|
21
|
+
* UI-компонент поля. Опционален: core-часть можно использовать без ссылки на компонент
|
|
22
|
+
* (значение и состояние работают без UI; компонент нужен только для рендеринга).
|
|
23
|
+
*/
|
|
24
|
+
component?: ComponentRef;
|
|
25
|
+
componentProps?: any;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
}
|
|
@@ -1,57 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type {
|
|
4
|
-
export type {
|
|
5
|
-
export type {
|
|
6
|
-
export type { FormSchema } from './deep-schema';
|
|
7
|
-
export type { FormSchemaNode, SchemaArrayControl } from './schema-node';
|
|
8
|
-
export type { FormControlsProxy, FormProxy, FormArrayProxy } from './form-proxy';
|
|
9
|
-
/**
|
|
10
|
-
* Конфигурация GroupNode.
|
|
11
|
-
*
|
|
12
|
-
* Под M1 группа создаётся из плоской {@link FormSchema} (дерево field-конфигов). Обёртка
|
|
13
|
-
* `{ form }` сохранена для совместимости вызова, legacy behavior/validation-схемы удалены (Ф7).
|
|
14
|
-
*
|
|
15
|
-
* @group Types
|
|
16
|
-
* @category Configuration Types
|
|
17
|
-
*/
|
|
18
|
-
export interface GroupNodeConfig<T> {
|
|
19
|
-
/** Схема структуры формы (поля и их конфигурация) */
|
|
20
|
-
form: FormSchema<T>;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Тип для Record с unknown значениями
|
|
24
|
-
* Используется вместо инлайнового `Record<string, unknown>`
|
|
25
|
-
* @internal
|
|
26
|
-
*/
|
|
27
|
-
export type UnknownRecord = Record<string, unknown>;
|
|
28
|
-
/**
|
|
29
|
-
* Интерфейс для узлов, похожих на ArrayNode (с методом at)
|
|
30
|
-
* Используется для duck typing при обходе путей
|
|
31
|
-
* @internal
|
|
32
|
-
*/
|
|
33
|
-
export interface ArrayNodeLike {
|
|
34
|
-
at(index: number): FormNode<unknown> | undefined;
|
|
35
|
-
length: unknown;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Конфиг с полем schema (для ArrayConfig)
|
|
39
|
-
* @internal
|
|
40
|
-
*/
|
|
41
|
-
export interface ConfigWithSchema {
|
|
42
|
-
schema: unknown;
|
|
43
|
-
initialItems?: unknown[];
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Конфиг с полем value (для извлечения значений)
|
|
47
|
-
* @internal
|
|
48
|
-
*/
|
|
49
|
-
export interface ConfigWithValue {
|
|
50
|
-
value: unknown;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Тип для проверки на функцию в conditional types
|
|
54
|
-
* Используется вместо Function для type narrowing
|
|
55
|
-
* @internal
|
|
56
|
-
*/
|
|
57
|
-
export type AnyFunction = (...args: never[]) => unknown;
|
|
1
|
+
export type { ValidationIssue } from './contracts';
|
|
2
|
+
export type { FieldConfig } from './field-config';
|
|
3
|
+
export type { RuleOptions } from './validation-schema';
|
|
4
|
+
export type { ComponentRef, FormSchemaNode, SchemaFieldNode, SchemaArrayNode, SchemaPartNode, SchemaContainerNode, SchemaChild, SchemaValueHandle, SchemaArrayHandle, SchemaGroupHandle, SchemaArrayControl, } from './schema-node';
|
|
5
|
+
export type { FormNode, FormControl, FormGroup, FormArray, FormFields } from './nodes';
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { ValidationIssue } from './contracts';
|
|
3
|
+
import { ComponentRef } from './schema-node';
|
|
4
|
+
/**
|
|
5
|
+
* Общий контракт ноды формы — поля, группы и массива под-форм.
|
|
6
|
+
*
|
|
7
|
+
* Значением нода не владеет: источник истины — модель (`model.$.<путь>`), нода держит состояние
|
|
8
|
+
* взаимодействия (`touched`, `dirty`), доступность и замечания валидации.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam T - Значение ноды
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const { form } = createForm<{ email: string }>({ initial: { email: '' } });
|
|
15
|
+
* form.email.setValue('user@mail.com');
|
|
16
|
+
* form.email.markAsTouched();
|
|
17
|
+
* form.valid.value; // true — замечаний нет
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface FormNode<T> {
|
|
21
|
+
/** Текущее значение. */
|
|
22
|
+
readonly value: ReadonlySignal<T>;
|
|
23
|
+
/** Замечаний, которые блокируют отправку, нет; нода включена и не ждёт проверки. */
|
|
24
|
+
readonly valid: ReadonlySignal<boolean>;
|
|
25
|
+
/** Есть блокирующие замечания; нода включена и не ждёт проверки. */
|
|
26
|
+
readonly invalid: ReadonlySignal<boolean>;
|
|
27
|
+
/** Идёт асинхронная проверка. */
|
|
28
|
+
readonly pending: ReadonlySignal<boolean>;
|
|
29
|
+
/** Нода отключена: её замечания сняты, в проверку она не входит. */
|
|
30
|
+
readonly disabled: ReadonlySignal<boolean>;
|
|
31
|
+
/** Пользователь взаимодействовал с нодой. */
|
|
32
|
+
readonly touched: ReadonlySignal<boolean>;
|
|
33
|
+
/** Значение меняли после создания или сброса. */
|
|
34
|
+
readonly dirty: ReadonlySignal<boolean>;
|
|
35
|
+
/** Замечания ноды; у группы и массива — свои и всех детей. */
|
|
36
|
+
readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Собственные замечания ноды — без замечаний детей: у группы и массива это правила на их ручке
|
|
39
|
+
* (`minLength` массива, `cross` группы), у поля — то же, что `issues`.
|
|
40
|
+
*/
|
|
41
|
+
readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
42
|
+
/** Значение без подписки. */
|
|
43
|
+
getValue(): T;
|
|
44
|
+
/** Записать значение в модель. */
|
|
45
|
+
setValue(value: T): void;
|
|
46
|
+
/**
|
|
47
|
+
* Вернуть значение и состояние к начальным; с аргументом — к переданному значению. В цель
|
|
48
|
+
* `compute` значение не пишется (кроме явного `reset(значение)` самого поля): ею владеет
|
|
49
|
+
* поведение — сбрасывается только состояние.
|
|
50
|
+
*/
|
|
51
|
+
reset(value?: T): void;
|
|
52
|
+
markAsTouched(): void;
|
|
53
|
+
markAsUntouched(): void;
|
|
54
|
+
enable(): void;
|
|
55
|
+
disable(): void;
|
|
56
|
+
/** Поставить замечания извне (ответ сервера, своя проверка). */
|
|
57
|
+
setIssues(issues: readonly ValidationIssue[]): void;
|
|
58
|
+
clearIssues(): void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Поле формы: нода над листом модели и состояние его компонента.
|
|
62
|
+
*
|
|
63
|
+
* @typeParam T - Значение поля
|
|
64
|
+
*/
|
|
65
|
+
export interface FormControl<T> extends FormNode<T> {
|
|
66
|
+
/** Компонент поля из узла схемы. */
|
|
67
|
+
readonly component: ComponentRef | undefined;
|
|
68
|
+
/** Пропсы компонента: из узла схемы и правок {@link FormControl.updateComponentProps}. */
|
|
69
|
+
readonly componentProps: ReadonlySignal<Readonly<Record<string, unknown>>>;
|
|
70
|
+
/**
|
|
71
|
+
* Показывать ли состояние ошибки (`aria-invalid`, рамка): поле невалидно и его трогали или
|
|
72
|
+
* меняли. Предупреждение (`severity: 'warning'`) поле невалидным не делает — тексты замечаний
|
|
73
|
+
* показывают по {@link FormControl.shouldShowIssues}.
|
|
74
|
+
*/
|
|
75
|
+
readonly shouldShowError: ReadonlySignal<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Показывать ли тексты замечаний: у поля есть замечания — ошибки или предупреждения, — и его
|
|
78
|
+
* трогали или меняли. Условия те же, что у {@link FormControl.shouldShowError} (не выключено,
|
|
79
|
+
* проверка не идёт), но в счёт идёт любое замечание, а не только блокирующее.
|
|
80
|
+
*/
|
|
81
|
+
readonly shouldShowIssues: ReadonlySignal<boolean>;
|
|
82
|
+
/** Слить правку в пропсы компонента (например, опции после загрузки справочника). */
|
|
83
|
+
updateComponentProps(patch: Record<string, unknown>): void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Массив под-форм: строка — {@link FormGroup} над элементом массива модели.
|
|
87
|
+
*
|
|
88
|
+
* Данными массив не владеет: мутации уходят в массив модели, формы строк держатся за под-модели
|
|
89
|
+
* и при `move`/`swap` не пересоздаются.
|
|
90
|
+
*
|
|
91
|
+
* Индексы (`at`, `keyAt`, `insert`, `removeAt`, `move`, `swap`) — по строкам формы. Элемент `null`
|
|
92
|
+
* в массиве модели строкой не становится, и индекс строки может не совпасть с индексом модели.
|
|
93
|
+
*
|
|
94
|
+
* @typeParam T - Элемент массива
|
|
95
|
+
*/
|
|
96
|
+
export interface FormArray<T extends object> extends FormNode<T[]> {
|
|
97
|
+
/** Число строк; чтение в эффекте или `computed` подписывает на изменения. */
|
|
98
|
+
readonly length: number;
|
|
99
|
+
at(index: number): FormGroup<T> | undefined;
|
|
100
|
+
map<R>(callback: (row: FormGroup<T>, index: number) => R): R[];
|
|
101
|
+
forEach(callback: (row: FormGroup<T>, index: number) => void): void;
|
|
102
|
+
/** Стабильный ключ строки: держится за элемент модели и не меняется при `move` и `swap`. */
|
|
103
|
+
keyAt(index: number): string;
|
|
104
|
+
/** Добавить строку; без значения — по шаблону массива модели (`arrayOf(blank)`). */
|
|
105
|
+
push(item?: T): void;
|
|
106
|
+
insert(index: number, item?: T): void;
|
|
107
|
+
removeAt(index: number): void;
|
|
108
|
+
move(from: number, to: number): void;
|
|
109
|
+
swap(first: number, second: number): void;
|
|
110
|
+
clear(): void;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Признак «объект-группа»: обычный объект, а не массив, дата, файл, функция или примитив.
|
|
114
|
+
* `extends object` ловит и `interface`, и `type` (интерфейс не приводится к `Record`).
|
|
115
|
+
* Значение неизвестной формы (`unknown`, `{}`) — поле: у него нет известных полей, и словарь
|
|
116
|
+
* `Record<string, unknown>` остаётся группой полей, а не группой групп — иначе такая группа не
|
|
117
|
+
* приводилась бы к `FormGroup<any>` внутри обобщённых типов (контекст React, элементы списка).
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
type IsGroupObject<V> = V extends ReadonlyArray<unknown> ? false : V extends Date | File | Blob ? false : V extends (...args: never[]) => unknown ? false : V extends object ? Record<never, never> extends V ? false : true : false;
|
|
122
|
+
/** `T` — это `any`. @internal */
|
|
123
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
124
|
+
/**
|
|
125
|
+
* Ноды полей группы по именам полей данных: массив объектов — {@link FormArray}, объект —
|
|
126
|
+
* {@link FormGroup}, остальное (примитивы, массивы примитивов, даты, файлы) — {@link FormControl}.
|
|
127
|
+
* Опциональность снята: нода есть у каждого поля, опционально лишь значение.
|
|
128
|
+
*
|
|
129
|
+
* `FormFields<any>` — нестрогий словарь: так группа любой формы приводится к `FormGroup<any>`
|
|
130
|
+
* (контекст React, обобщённые компоненты), а не упирается в отображение полей типа `any`.
|
|
131
|
+
*
|
|
132
|
+
* @typeParam T - Значение группы
|
|
133
|
+
*/
|
|
134
|
+
export type FormFields<T> = IsAny<T> extends true ? Record<string, any> : {
|
|
135
|
+
[K in keyof T]-?: NonNullable<T[K]> extends ReadonlyArray<infer U> ? IsGroupObject<U> extends true ? FormArray<U & object> : FormControl<T[K]> : IsGroupObject<NonNullable<T[K]>> extends true ? FormGroup<NonNullable<T[K]>> : FormControl<T[K]>;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Группа полей: нода над группой модели и доступ к нодам полей.
|
|
139
|
+
*
|
|
140
|
+
* `form.email` — нода поля; поле с именем члена {@link FormNode} (`value`, `issues`, `reset`, …)
|
|
141
|
+
* недостижимо точкой и доступно через `form.$`: `form.$.value`. Остальные имена полей данных
|
|
142
|
+
* (`id`, `status`, `fields`, `submit`, …) ничем не затенены.
|
|
143
|
+
*
|
|
144
|
+
* @typeParam T - Значение группы
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* interface Order { status: string; value: number; customer: { name: string } }
|
|
149
|
+
* const { form } = createForm<Order>({ initial: { status: 'new', value: 0, customer: { name: '' } } });
|
|
150
|
+
* form.status.setValue('paid'); // поле данных
|
|
151
|
+
* form.$.value.setValue(100); // поле, затенённое членом ноды
|
|
152
|
+
* form.customer.name.markAsTouched();
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export type FormGroup<T> = FormNode<T> & Omit<FormFields<T>, keyof FormNode<T>> & {
|
|
156
|
+
/** Ноды всех полей по имени, включая затенённые членами {@link FormNode}. */
|
|
157
|
+
readonly $: FormFields<T>;
|
|
158
|
+
};
|
|
159
|
+
export {};
|