@reformer/core 11.0.0-beta.1 → 11.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -68
- package/dist/behaviors-value-D05z3my6.js +109 -0
- package/dist/behaviors.d.ts +1 -1
- package/dist/behaviors.js +376 -211
- package/dist/create-model-DPSG3I0O.js +30 -0
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/en-HP8XEych.js +44 -0
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +84 -0
- package/dist/form/behaviors/context.d.ts +56 -0
- package/dist/form/behaviors/index.d.ts +30 -0
- package/dist/form/behaviors/internals.d.ts +41 -0
- package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +28 -5
- package/dist/form/behaviors/operators.d.ts +59 -0
- package/dist/form/behaviors/schema-operators.d.ts +83 -0
- package/dist/form/behaviors/types.d.ts +37 -0
- package/dist/form/create-form.d.ts +59 -70
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +153 -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 +23 -24
- 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 +159 -0
- package/dist/form/schema-subtree.d.ts +28 -0
- package/dist/form/signal-node-registry.d.ts +27 -22
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +21 -85
- 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 +147 -46
- package/dist/form/types/validation-schema.d.ts +25 -17
- package/dist/form/validation/context.d.ts +67 -0
- package/dist/form/validation/index.d.ts +24 -28
- package/dist/form/validation/operators.d.ts +101 -0
- package/dist/form/validation/run.d.ts +120 -0
- package/dist/form/validation/strategy.d.ts +89 -0
- package/dist/form/validation/types.d.ts +83 -0
- package/dist/form/validators/date-utils.d.ts +42 -0
- package/dist/form/validators/email.d.ts +23 -0
- package/dist/form/{validation/validators → validators}/file-type.d.ts +10 -10
- package/dist/form/{validation/validators → validators}/file-utils.d.ts +3 -2
- package/dist/form/validators/future-date.d.ts +23 -0
- package/dist/form/{validation/validators → validators}/index.d.ts +7 -4
- package/dist/form/validators/integer.d.ts +22 -0
- package/dist/form/validators/is-date.d.ts +24 -0
- package/dist/form/validators/is-number.d.ts +25 -0
- package/dist/form/validators/max-age.d.ts +28 -0
- package/dist/form/validators/max-date.d.ts +25 -0
- package/dist/form/{validation/validators → validators}/max-file-size.d.ts +10 -10
- package/dist/form/validators/max-files.d.ts +26 -0
- package/dist/form/validators/max-length.d.ts +33 -0
- package/dist/form/validators/max-total-file-size.d.ts +26 -0
- package/dist/form/validators/max.d.ts +26 -0
- package/dist/form/validators/min-age.d.ts +27 -0
- package/dist/form/validators/min-date.d.ts +25 -0
- package/dist/form/{validation/validators → validators}/min-file-size.d.ts +10 -10
- package/dist/form/validators/min-files.d.ts +27 -0
- package/dist/form/validators/min-length.d.ts +33 -0
- package/dist/form/validators/min.d.ts +26 -0
- package/dist/form/validators/multiple-of.d.ts +25 -0
- package/dist/form/validators/non-negative.d.ts +22 -0
- package/dist/form/validators/non-zero.d.ts +22 -0
- package/dist/form/validators/past-date.d.ts +23 -0
- package/dist/form/validators/pattern.d.ts +26 -0
- package/dist/form/{validation/validators → validators}/phone.d.ts +11 -11
- package/dist/form/validators/required.d.ts +29 -0
- package/dist/form/validators/url.d.ts +45 -0
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +54 -0
- package/dist/i18n/en.json.d.ts +49 -0
- package/dist/i18n/loader.d.ts +71 -0
- package/dist/i18n/locale-store.d.ts +46 -0
- package/dist/i18n/locale.d.ts +90 -0
- package/dist/i18n/message-format.d.ts +136 -0
- package/dist/i18n/resolve-localized.d.ts +17 -0
- package/dist/i18n/ru.json.d.ts +49 -0
- package/dist/i18n/translator.d.ts +61 -0
- package/dist/i18n/validation-message.d.ts +53 -0
- package/dist/i18n.d.ts +26 -0
- package/dist/i18n.js +86 -0
- package/dist/index.d.ts +17 -5
- package/dist/index.js +942 -2601
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.d.ts +2 -0
- package/dist/locale/en.js +5 -0
- package/dist/locale/en.json +45 -0
- package/dist/locale/index.d.ts +18 -0
- package/dist/locale/ru.d.ts +2 -0
- package/dist/locale/ru.js +44 -0
- package/dist/locale/ru.json +45 -0
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.d.ts +2 -0
- package/dist/locale.js +9 -0
- package/dist/{state → model}/behaviors-value.d.ts +24 -6
- package/dist/{state/form-model.d.ts → model/create-model.d.ts} +18 -4
- package/dist/{state → model}/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +42 -0
- package/dist/model/model-nodes.d.ts +190 -0
- package/dist/model/model-signals-proxy.d.ts +63 -0
- package/dist/model/model-value-proxy.d.ts +84 -0
- package/dist/{state → model}/safe-effect.d.ts +50 -2
- package/dist/model/types.d.ts +239 -0
- package/dist/model.d.ts +2 -0
- package/dist/model.js +27 -0
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
- package/dist/platforms/react/field-binding/field-handle.d.ts +53 -0
- package/dist/platforms/react/form-bundle-context.d.ts +27 -0
- package/dist/{form → platforms/react}/hooks/types.d.ts +58 -36
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +41 -0
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +13 -7
- package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +6 -6
- package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +7 -9
- package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +18 -18
- package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +18 -0
- package/dist/platforms/react/i18n/hooks.d.ts +65 -0
- package/dist/platforms/react/i18n/provider.d.ts +59 -0
- package/dist/platforms/react/index.d.ts +30 -0
- 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-BazLOv1g.js +669 -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-DzvnsWLh.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.d.ts +1 -1
- package/dist/validation.js +11 -165
- package/dist/validators/email.d.ts +1 -1
- package/dist/validators/email.js +5 -8
- package/dist/validators/future-date.d.ts +1 -1
- package/dist/validators/future-date.js +10 -13
- package/dist/validators/integer.d.ts +1 -1
- package/dist/validators/integer.js +4 -7
- package/dist/validators/is-date.d.ts +1 -1
- package/dist/validators/is-date.js +5 -8
- package/dist/validators/is-number.d.ts +1 -1
- package/dist/validators/is-number.js +4 -7
- package/dist/validators/max-age.d.ts +1 -1
- package/dist/validators/max-age.js +8 -12
- package/dist/validators/max-date.d.ts +1 -1
- package/dist/validators/max-date.js +10 -13
- package/dist/validators/max-length.d.ts +1 -1
- package/dist/validators/max-length.js +7 -10
- package/dist/validators/max.d.ts +1 -1
- package/dist/validators/max.js +4 -7
- package/dist/validators/min-age.d.ts +1 -1
- package/dist/validators/min-age.js +10 -11
- package/dist/validators/min-date.d.ts +1 -1
- package/dist/validators/min-date.js +8 -11
- package/dist/validators/min-length.d.ts +1 -1
- package/dist/validators/min-length.js +4 -7
- package/dist/validators/min.d.ts +1 -1
- package/dist/validators/min.js +4 -7
- package/dist/validators/multiple-of.d.ts +1 -1
- package/dist/validators/multiple-of.js +11 -10
- package/dist/validators/non-negative.d.ts +1 -1
- package/dist/validators/non-negative.js +2 -5
- package/dist/validators/non-zero.d.ts +1 -1
- package/dist/validators/non-zero.js +4 -7
- package/dist/validators/past-date.d.ts +1 -1
- package/dist/validators/past-date.js +10 -13
- package/dist/validators/pattern.d.ts +1 -1
- package/dist/validators/pattern.js +5 -8
- package/dist/validators/phone.d.ts +1 -1
- package/dist/validators/phone.js +10 -11
- package/dist/validators/required.d.ts +1 -1
- package/dist/validators/required.js +4 -7
- package/dist/validators/url.d.ts +1 -1
- package/dist/validators/url.js +48 -17
- package/dist/validators.d.ts +1 -1
- package/dist/validators.js +86 -108
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +112 -74
- package/docs/llms/03-api-signatures.md +369 -148
- 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 +132 -84
- package/docs/llms/10-arrays.md +141 -61
- 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 +224 -70
- package/docs/llms/16-ui-components.md +86 -46
- package/docs/llms/17-nonexistent-api.md +217 -32
- package/docs/llms/18-conditional-fields.md +71 -32
- package/docs/llms/19-reading-values.md +45 -4
- package/docs/llms/20-compute-vs-watch.md +25 -10
- package/docs/llms/21-array-operations.md +76 -26
- 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 +278 -128
- package/docs/llms/29-async-preload.md +41 -31
- 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 +178 -66
- package/docs/llms/34-i18n.md +298 -0
- package/docs/llms/35-unified-contract.md +309 -0
- 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 +396 -0
- package/llms-index.json +1 -0
- package/llms.txt +10690 -3089
- package/package.json +48 -9
- package/dist/behaviors-node-DGEFMTQu.js +0 -18
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/derived-registry-CluZCQ7U.js +0 -16
- package/dist/form/behaviors.d.ts +0 -133
- package/dist/form/error-handler.d.ts +0 -166
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -188
- 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 -294
- 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 -121
- 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/validators/date-utils.d.ts +0 -30
- package/dist/form/validation/validators/email.d.ts +0 -23
- package/dist/form/validation/validators/future-date.d.ts +0 -23
- package/dist/form/validation/validators/integer.d.ts +0 -22
- package/dist/form/validation/validators/is-date.d.ts +0 -23
- package/dist/form/validation/validators/is-number.d.ts +0 -24
- package/dist/form/validation/validators/max-age.d.ts +0 -25
- package/dist/form/validation/validators/max-date.d.ts +0 -25
- package/dist/form/validation/validators/max-files.d.ts +0 -26
- package/dist/form/validation/validators/max-length.d.ts +0 -27
- package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
- package/dist/form/validation/validators/max.d.ts +0 -25
- package/dist/form/validation/validators/min-age.d.ts +0 -25
- package/dist/form/validation/validators/min-date.d.ts +0 -25
- package/dist/form/validation/validators/min-files.d.ts +0 -26
- package/dist/form/validation/validators/min-length.d.ts +0 -27
- package/dist/form/validation/validators/min.d.ts +0 -25
- package/dist/form/validation/validators/multiple-of.d.ts +0 -24
- package/dist/form/validation/validators/non-negative.d.ts +0 -22
- package/dist/form/validation/validators/non-zero.d.ts +0 -22
- package/dist/form/validation/validators/past-date.d.ts +0 -23
- package/dist/form/validation/validators/pattern.d.ts +0 -33
- package/dist/form/validation/validators/required.d.ts +0 -32
- package/dist/form/validation/validators/url.d.ts +0 -43
- package/dist/form/validation-schema.d.ts +0 -106
- package/dist/form/validation-strategy.d.ts +0 -55
- package/dist/form/validators.d.ts +0 -11
- package/dist/form-model-DYCzGGKF.js +0 -286
- package/dist/state/index.d.ts +0 -31
- package/dist/state/subscription-manager.d.ts +0 -180
- package/dist/state/types.d.ts +0 -144
- package/dist/state.d.ts +0 -2
- package/dist/state.js +0 -21
- package/dist/subscription-manager-705bls97.js +0 -171
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import { Signal, ReadonlySignal } from '@preact/signals-core';
|
|
2
|
-
import { FormNode, SetValueOptions } from './form-node';
|
|
3
|
-
import { ValidationError, FieldStatus, FormSchema, GroupNodeConfig, FormValue } from '../types/index';
|
|
4
|
-
import { FormProxy } from '../types/form-proxy';
|
|
5
|
-
import { SubmitOptions, SubmitResult } from '../form-submitter';
|
|
6
|
-
/**
|
|
7
|
-
* GroupNode - узел для группы полей
|
|
8
|
-
*
|
|
9
|
-
* Создаётся из {@link FormSchema} (дерево field-конфигов). Обычно строится через `createForm`
|
|
10
|
-
* (M1: `createForm({ model, schema })`); schema-валидация/behavior живут на слое модели
|
|
11
|
-
* (`validateModel` из `@reformer/core/validation`, `computeFrom`/`enableWhen`/…), а не на ноде.
|
|
12
|
-
*
|
|
13
|
-
* @group Nodes
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* const form = new GroupNode({
|
|
18
|
-
* email: { valueSignal: model.$.email, component: Input },
|
|
19
|
-
* password: { valueSignal: model.$.password, component: Input },
|
|
20
|
-
* });
|
|
21
|
-
*
|
|
22
|
-
* // Прямой доступ к полям через Proxy
|
|
23
|
-
* const proxy = form.getProxy();
|
|
24
|
-
* proxy.email.setValue('test@mail.com');
|
|
25
|
-
* await proxy.validate();
|
|
26
|
-
* console.log(proxy.valid.value);
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export declare class GroupNode<T> extends FormNode<T> {
|
|
30
|
-
id: `${string}-${string}-${string}-${string}-${string}`;
|
|
31
|
-
/**
|
|
32
|
-
* Коллекция полей формы (упрощённый Map вместо FieldRegistry)
|
|
33
|
-
*/
|
|
34
|
-
private readonly _fields;
|
|
35
|
-
/**
|
|
36
|
-
* Менеджер подписок для централизованного cleanup
|
|
37
|
-
*/
|
|
38
|
-
private disposers;
|
|
39
|
-
/**
|
|
40
|
-
* Ссылка на Proxy-инстанс
|
|
41
|
-
*/
|
|
42
|
-
private _proxyInstance?;
|
|
43
|
-
/**
|
|
44
|
-
* Cleanup декларативной схемы поведения (createForm({ behavior })). Вызывается в dispose().
|
|
45
|
-
*/
|
|
46
|
-
private _behaviorCleanup?;
|
|
47
|
-
/**
|
|
48
|
-
* Фабрика для создания узлов формы
|
|
49
|
-
*/
|
|
50
|
-
private readonly nodeFactory;
|
|
51
|
-
/** Управление отправкой формы */
|
|
52
|
-
private readonly formSubmitter;
|
|
53
|
-
/** Флаг disabled состояния */
|
|
54
|
-
private readonly _disabled;
|
|
55
|
-
/** Form-level validation errors */
|
|
56
|
-
private readonly _formErrors;
|
|
57
|
-
/**
|
|
58
|
-
* M1: связь листовой ноды-ребёнка → её сигнал модели (тот, что помечает `markDerived`).
|
|
59
|
-
* Нужна bulk-сеттерам (`setValue`/`patchValue`), чтобы корректно определять derived-поля:
|
|
60
|
-
* `field.value` — computed-обёртка, отличная от записываемого сигнала модели. @internal
|
|
61
|
-
*/
|
|
62
|
-
private readonly _fieldSignals;
|
|
63
|
-
readonly value: ReadonlySignal<T>;
|
|
64
|
-
readonly valid: ReadonlySignal<boolean>;
|
|
65
|
-
readonly invalid: ReadonlySignal<boolean>;
|
|
66
|
-
readonly touched: ReadonlySignal<boolean>;
|
|
67
|
-
readonly dirty: ReadonlySignal<boolean>;
|
|
68
|
-
readonly pending: ReadonlySignal<boolean>;
|
|
69
|
-
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
70
|
-
readonly status: ReadonlySignal<FieldStatus>;
|
|
71
|
-
readonly submitting: ReadonlySignal<boolean>;
|
|
72
|
-
/**
|
|
73
|
-
* Создать GroupNode только со схемой формы (обратная совместимость)
|
|
74
|
-
*/
|
|
75
|
-
constructor(schema: FormSchema<T>);
|
|
76
|
-
/**
|
|
77
|
-
* Создать GroupNode с полной конфигурацией (form, behavior, validation)
|
|
78
|
-
*/
|
|
79
|
-
constructor(config: GroupNodeConfig<T>);
|
|
80
|
-
/**
|
|
81
|
-
* Создать Proxy для типобезопасного доступа к полям
|
|
82
|
-
* @see buildFormProxy
|
|
83
|
-
*/
|
|
84
|
-
private buildProxy;
|
|
85
|
-
getValue(): T;
|
|
86
|
-
setValue(value: T, options?: SetValueOptions): void;
|
|
87
|
-
patchValue(value: Partial<T>): void;
|
|
88
|
-
/**
|
|
89
|
-
* Сбросить форму к указанным значениям (или к initialValues)
|
|
90
|
-
*
|
|
91
|
-
* @param value - опциональный объект со значениями для сброса
|
|
92
|
-
*
|
|
93
|
-
* @remarks
|
|
94
|
-
* Рекурсивно вызывает reset() для всех полей формы
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```typescript
|
|
98
|
-
* // Сброс к initialValues
|
|
99
|
-
* form.reset();
|
|
100
|
-
*
|
|
101
|
-
* // Сброс к новым значениям
|
|
102
|
-
* form.reset({ email: 'new@mail.com', password: '' });
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
reset(value?: T): void;
|
|
106
|
-
/**
|
|
107
|
-
* Сбросить форму к исходным значениям (initialValues)
|
|
108
|
-
*/
|
|
109
|
-
resetToInitial(): void;
|
|
110
|
-
validate(): Promise<boolean>;
|
|
111
|
-
/**
|
|
112
|
-
* Установить form-level validation errors
|
|
113
|
-
*/
|
|
114
|
-
setErrors(errors: ValidationError[]): void;
|
|
115
|
-
/**
|
|
116
|
-
* Очистить все errors (form-level + field-level)
|
|
117
|
-
*/
|
|
118
|
-
clearErrors(): void;
|
|
119
|
-
/**
|
|
120
|
-
* Получить поле по ключу
|
|
121
|
-
*/
|
|
122
|
-
getField<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
|
|
123
|
-
/**
|
|
124
|
-
* Получить Map всех полей формы (для совместимости)
|
|
125
|
-
*/
|
|
126
|
-
get fields(): Map<keyof T, FormNode<FormValue>>;
|
|
127
|
-
/**
|
|
128
|
-
* Получить Proxy-инстанс для прямого доступа к полям
|
|
129
|
-
*
|
|
130
|
-
* Proxy позволяет обращаться к полям формы напрямую через точечную нотацию:
|
|
131
|
-
* - form.email вместо form.fields.get('email')
|
|
132
|
-
* - form.address.city вместо form.fields.get('address').fields.get('city')
|
|
133
|
-
*
|
|
134
|
-
* @returns Proxy-инстанс с типобезопасным доступом к полям или сама форма, если proxy не доступен
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```typescript
|
|
138
|
-
* const form = new GroupNode({
|
|
139
|
-
* email: { value: '', component: Input },
|
|
140
|
-
* name: { value: '', component: Input },
|
|
141
|
-
* });
|
|
142
|
-
*
|
|
143
|
-
* const proxy = form.getProxy();
|
|
144
|
-
* console.log(proxy.email.value.value); // Прямой доступ к полю
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
|
-
getProxy(): FormProxy<T>;
|
|
148
|
-
/**
|
|
149
|
-
* Получить все поля формы как итератор
|
|
150
|
-
*/
|
|
151
|
-
getAllFields(): IterableIterator<FormNode<FormValue>>;
|
|
152
|
-
protected onMarkAsTouched(): void;
|
|
153
|
-
protected onMarkAsUntouched(): void;
|
|
154
|
-
protected onMarkAsDirty(): void;
|
|
155
|
-
protected onMarkAsPristine(): void;
|
|
156
|
-
/**
|
|
157
|
-
* Отправить форму
|
|
158
|
-
*
|
|
159
|
-
* @param onSubmit - Callback для отправки данных
|
|
160
|
-
* @param options - Опции submit (skipValidation, skipTouch)
|
|
161
|
-
* @returns Результат от onSubmit или `null` если валидация не пройдена
|
|
162
|
-
*
|
|
163
|
-
* @remarks
|
|
164
|
-
* `null` перегружен: он означает и «валидация не пройдена», и легитимный `null`-результат
|
|
165
|
-
* `onSubmit` (или void-обработчик). Если вызывающей стороне нужно различать эти случаи —
|
|
166
|
-
* используйте {@link submitWithResult}, который возвращает явный флаг `success`.
|
|
167
|
-
*/
|
|
168
|
-
submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
|
|
169
|
-
/**
|
|
170
|
-
* Отправить форму с расширенным результатом
|
|
171
|
-
*
|
|
172
|
-
* @param onSubmit - Callback для отправки данных
|
|
173
|
-
* @param options - Опции submit
|
|
174
|
-
* @returns Объект SubmitResult с данными, статусом и возможной ошибкой
|
|
175
|
-
*/
|
|
176
|
-
submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
|
|
177
|
-
/**
|
|
178
|
-
* Получить вложенное поле по пути
|
|
179
|
-
*
|
|
180
|
-
* Поддерживаемые форматы путей:
|
|
181
|
-
* - Simple: "email" - получить поле верхнего уровня
|
|
182
|
-
* - Nested: "address.city" - получить вложенное поле
|
|
183
|
-
* - Array index: "items[0]" - получить элемент массива по индексу
|
|
184
|
-
* - Combined: "items[0].name" - получить поле элемента массива
|
|
185
|
-
*
|
|
186
|
-
* @param path - Путь к полю
|
|
187
|
-
* @returns FormNode если найдено, undefined если путь не существует
|
|
188
|
-
*
|
|
189
|
-
* @example
|
|
190
|
-
* ```typescript
|
|
191
|
-
* const form = new GroupNode({
|
|
192
|
-
* email: { value: '', component: Input },
|
|
193
|
-
* address: {
|
|
194
|
-
* city: { value: '', component: Input }
|
|
195
|
-
* },
|
|
196
|
-
* items: [{ name: { value: '', component: Input } }]
|
|
197
|
-
* });
|
|
198
|
-
*
|
|
199
|
-
* form.getFieldByPath('email'); // FieldNode
|
|
200
|
-
* form.getFieldByPath('address.city'); // FieldNode
|
|
201
|
-
* form.getFieldByPath('items[0]'); // GroupNode
|
|
202
|
-
* form.getFieldByPath('items[0].name'); // FieldNode
|
|
203
|
-
* form.getFieldByPath('invalid.path'); // undefined
|
|
204
|
-
* ```
|
|
205
|
-
*/
|
|
206
|
-
getFieldByPath(path: string): FormNode<FormValue> | undefined;
|
|
207
|
-
/**
|
|
208
|
-
* Создать узел на основе конфигурации
|
|
209
|
-
*
|
|
210
|
-
* ✅ РЕФАКТОРИНГ: Полное делегирование NodeFactory
|
|
211
|
-
*
|
|
212
|
-
* NodeFactory теперь обрабатывает:
|
|
213
|
-
* - Массивы [schema, ...items]
|
|
214
|
-
* - FieldConfig
|
|
215
|
-
* - GroupConfig
|
|
216
|
-
* - ArrayConfig
|
|
217
|
-
*
|
|
218
|
-
* @param config Конфигурация узла
|
|
219
|
-
* @returns Созданный узел формы
|
|
220
|
-
* @private
|
|
221
|
-
*/
|
|
222
|
-
private createNode;
|
|
223
|
-
/**
|
|
224
|
-
* Связывает два поля: при изменении source автоматически обновляется target
|
|
225
|
-
*/
|
|
226
|
-
linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
|
|
227
|
-
/**
|
|
228
|
-
* Подписка на изменения вложенного поля по строковому пути
|
|
229
|
-
* Поддерживает вложенные пути типа "address.city"
|
|
230
|
-
*
|
|
231
|
-
* @param fieldPath - Строковый путь к полю (например, "address.city")
|
|
232
|
-
* @param callback - Функция, вызываемая при изменении поля
|
|
233
|
-
* @returns Функция отписки для cleanup
|
|
234
|
-
*
|
|
235
|
-
* @example
|
|
236
|
-
* ```typescript
|
|
237
|
-
* // Подписка на изменение страны для загрузки городов
|
|
238
|
-
* const dispose = form.watchField(
|
|
239
|
-
* 'registrationAddress.country',
|
|
240
|
-
* async (countryCode) => {
|
|
241
|
-
* if (countryCode) {
|
|
242
|
-
* const cities = await fetchCitiesByCountry(countryCode);
|
|
243
|
-
* form.registrationAddress.city.updateComponentProps({
|
|
244
|
-
* options: cities
|
|
245
|
-
* });
|
|
246
|
-
* }
|
|
247
|
-
* }
|
|
248
|
-
* );
|
|
249
|
-
*
|
|
250
|
-
* // Cleanup
|
|
251
|
-
* useEffect(() => dispose, []);
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
|
-
/** Подписка на top-level поле — value типизирован как `T[K]`. */
|
|
255
|
-
watchField<K extends keyof T & string>(fieldPath: K, callback: (value: T[K]) => void | Promise<void>): () => void;
|
|
256
|
-
/**
|
|
257
|
-
* Подписка на вложенное поле по строковому пути ("address.city").
|
|
258
|
-
* Путь нельзя выразить в типах узла → value честно `unknown`, потребитель сужает.
|
|
259
|
-
*/
|
|
260
|
-
watchFieldByPath(path: string, callback: (value: unknown) => void | Promise<void>): () => void;
|
|
261
|
-
/**
|
|
262
|
-
* Hook: вызывается после disable()
|
|
263
|
-
*/
|
|
264
|
-
protected onDisable(): void;
|
|
265
|
-
/**
|
|
266
|
-
* Hook: вызывается после enable()
|
|
267
|
-
*/
|
|
268
|
-
protected onEnable(): void;
|
|
269
|
-
/**
|
|
270
|
-
* Прикрепить cleanup декларативной схемы поведения (вызывается createForm).
|
|
271
|
-
* @internal
|
|
272
|
-
*/
|
|
273
|
-
attachBehaviorCleanup(cleanup: () => void): void;
|
|
274
|
-
/**
|
|
275
|
-
* Связать листовую ноду-ребёнка с её сигналом модели (M1). Вызывается `createForm` для каждого
|
|
276
|
-
* листового поля на его владеющей группе — используется bulk-сеттерами для derived-guard (F9).
|
|
277
|
-
* @internal
|
|
278
|
-
*/
|
|
279
|
-
registerFieldSignal(node: FormNode<any>, signal: Signal<unknown>): void;
|
|
280
|
-
/**
|
|
281
|
-
* Очистить все ресурсы узла
|
|
282
|
-
*/
|
|
283
|
-
dispose(): void;
|
|
284
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { Signal, ReadonlySignal } from '@preact/signals-core';
|
|
2
|
-
import { FormNode } from './form-node';
|
|
3
|
-
import { FieldStatus, ValidationError } from '../types/index';
|
|
4
|
-
import { FormModel } from '../../state/types';
|
|
5
|
-
import { FormProxy } from '../types/form-proxy';
|
|
6
|
-
/** Минимальный контракт реактивного массива модели, используемый узлом. */
|
|
7
|
-
export interface ModelArrayControl<TItem extends object> {
|
|
8
|
-
readonly length: number;
|
|
9
|
-
at(index: number): FormModel<TItem> | undefined;
|
|
10
|
-
push(item: TItem): void;
|
|
11
|
-
insertAt(index: number, item: TItem): void;
|
|
12
|
-
removeAt(index: number): void;
|
|
13
|
-
move(from: number, to: number): void;
|
|
14
|
-
swap(a: number, b: number): void;
|
|
15
|
-
clear(): void;
|
|
16
|
-
toArray(): TItem[];
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Узел массива, делегирующий данные массиву {@link FormModel} (архитектура M1).
|
|
20
|
-
*
|
|
21
|
-
* В отличие от {@link ArrayNode} (владеет элементами сам), `ModelArrayNode` НЕ владеет данными:
|
|
22
|
-
* массив принадлежит модели, а узел держит per-item формы элементов (привязанные к сигналам
|
|
23
|
-
* под-моделей) и синхронизирует их с длиной массива модели. Мутации (`push`/`removeAt`/`move`/…)
|
|
24
|
-
* делегируются массиву модели; per-item формы кэшируются по идентичности под-модели, поэтому при
|
|
25
|
-
* reorder/повторном рендере не пересоздаются (состояние и валидация сохраняются). Реализует тот же
|
|
26
|
-
* контракт, что ждут секции массива и `useFormControl` (`length`/`value`/`valid`/`errors`/`at`/`push`/…).
|
|
27
|
-
*
|
|
28
|
-
* Обычно создаётся не напрямую, а `createForm({ model, schema })`: когда в схеме встречается узел
|
|
29
|
-
* массива `{ array: model.<field>, item: (item) => itemSchema }`, форма материализует его как
|
|
30
|
-
* `ModelArrayNode` и кладёт под `form.<field>` (совместим с `FormArraySection`).
|
|
31
|
-
*
|
|
32
|
-
* @group Nodes
|
|
33
|
-
*
|
|
34
|
-
* @example Массив как часть формы (через createForm)
|
|
35
|
-
* ```typescript
|
|
36
|
-
* const model = createModel<{ rows: { name: string; qty: number }[] }>({ rows: [] });
|
|
37
|
-
*
|
|
38
|
-
* const rowItem = (item: FormModel<{ name: string; qty: number }>) => ({
|
|
39
|
-
* name: { value: item.$.name, component: Input },
|
|
40
|
-
* qty: { value: item.$.qty, component: Input },
|
|
41
|
-
* });
|
|
42
|
-
*
|
|
43
|
-
* const form = createForm({
|
|
44
|
-
* model,
|
|
45
|
-
* schema: {
|
|
46
|
-
* children: [{ array: model.rows, item: rowItem }],
|
|
47
|
-
* },
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* const rows = form.rows as unknown as ModelArrayNode<{ name: string; qty: number }>;
|
|
51
|
-
* rows.push({ name: 'A', qty: 1 }); // мутация уезжает в model.rows
|
|
52
|
-
* rows.at(0)?.name.setValue('B'); // правка поля элемента доезжает в под-модель
|
|
53
|
-
* rows.length.value; // 1 (реактивная длина)
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
export declare class ModelArrayNode<T extends object> extends FormNode<T[]> {
|
|
57
|
-
private readonly control;
|
|
58
|
-
private readonly buildItem;
|
|
59
|
-
private readonly itemNodes;
|
|
60
|
-
private readonly cache;
|
|
61
|
-
private mountedModels;
|
|
62
|
-
private readonly initial;
|
|
63
|
-
private readonly _arrayErrors;
|
|
64
|
-
private readonly disposeSync;
|
|
65
|
-
/**
|
|
66
|
-
* Сигнал per-item форм. Публичен в т.ч. чтобы `isArrayNode` распознавал узел как массив
|
|
67
|
-
* (duck-typing проверяет наличие `items`).
|
|
68
|
-
*/
|
|
69
|
-
get items(): Signal<FormProxy<T>[]>;
|
|
70
|
-
readonly value: ReadonlySignal<T[]>;
|
|
71
|
-
readonly valid: ReadonlySignal<boolean>;
|
|
72
|
-
readonly invalid: ReadonlySignal<boolean>;
|
|
73
|
-
readonly pending: ReadonlySignal<boolean>;
|
|
74
|
-
readonly touched: ReadonlySignal<boolean>;
|
|
75
|
-
readonly dirty: ReadonlySignal<boolean>;
|
|
76
|
-
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
77
|
-
readonly status: ReadonlySignal<FieldStatus>;
|
|
78
|
-
readonly length: ReadonlySignal<number>;
|
|
79
|
-
/**
|
|
80
|
-
* @param control Реактивный массив модели (`model.<path>`).
|
|
81
|
-
* @param buildItem Строитель формы элемента по его под-модели (инъекция, чтобы избежать цикла
|
|
82
|
-
* импорта с `create-form`). Обычно `(item) => createForm({ model: item, schema: itemSchema(item) })`.
|
|
83
|
-
*/
|
|
84
|
-
constructor(control: ModelArrayControl<T>, buildItem: (item: FormModel<T>) => FormProxy<T>);
|
|
85
|
-
at(index: number): FormProxy<T> | undefined;
|
|
86
|
-
map<R>(fn: (item: FormProxy<T>, index: number) => R): R[];
|
|
87
|
-
forEach(fn: (item: FormProxy<T>, index: number) => void): void;
|
|
88
|
-
push(item?: Partial<T>): void;
|
|
89
|
-
insert(index: number, item?: Partial<T>): void;
|
|
90
|
-
removeAt(index: number): void;
|
|
91
|
-
/**
|
|
92
|
-
* Переместить элемент модель-массива. Делегирует мутацию массиву модели; внутренний `effect`
|
|
93
|
-
* пересоберёт `itemNodes` в новом порядке (per-item формы сохраняются по кешу идентичности).
|
|
94
|
-
*/
|
|
95
|
-
move(from: number, to: number): void;
|
|
96
|
-
/** Поменять местами два элемента модель-массива (см. {@link move}). */
|
|
97
|
-
swap(a: number, b: number): void;
|
|
98
|
-
clear(): void;
|
|
99
|
-
getValue(): T[];
|
|
100
|
-
setValue(values: T[]): void;
|
|
101
|
-
patchValue(values: Partial<T[]>): void;
|
|
102
|
-
reset(values?: T[]): void;
|
|
103
|
-
resetToInitial(): void;
|
|
104
|
-
validate(): Promise<boolean>;
|
|
105
|
-
setErrors(errors: ValidationError[]): void;
|
|
106
|
-
clearErrors(): void;
|
|
107
|
-
protected onMarkAsTouched(): void;
|
|
108
|
-
protected onMarkAsUntouched(): void;
|
|
109
|
-
protected onMarkAsDirty(): void;
|
|
110
|
-
protected onMarkAsPristine(): void;
|
|
111
|
-
protected onDisable(): void;
|
|
112
|
-
protected onEnable(): void;
|
|
113
|
-
/** Диспозит форму элемента (эффекты/поведения/вложенные подписки); терпима к не-GroupNode. */
|
|
114
|
-
private disposeItemNode;
|
|
115
|
-
/**
|
|
116
|
-
* Очистка подписки синхронизации И детерминированный teardown всех форм элементов.
|
|
117
|
-
* Без пробрасывания dispose в itemNodes эффекты/поведения каждой формы элемента (в т.ч. disposeSync
|
|
118
|
-
* вложенных ModelArrayNode) остались бы жить после teardown формы — утечка подписок.
|
|
119
|
-
*/
|
|
120
|
-
dispose(): void;
|
|
121
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
-
import { FieldStatus } from './types/index';
|
|
3
|
-
/**
|
|
4
|
-
* FormStatusMachine - управляет состоянием поля формы
|
|
5
|
-
*
|
|
6
|
-
* Предоставляет:
|
|
7
|
-
* - Единый источник истины для статуса
|
|
8
|
-
* - Computed signals для derived состояний (valid, invalid, pending, disabled)
|
|
9
|
-
* - Валидацию переходов между состояниями
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* const statusMachine = new FormStatusMachine('valid');
|
|
14
|
-
*
|
|
15
|
-
* // Начало валидации
|
|
16
|
-
* statusMachine.startValidation();
|
|
17
|
-
* console.log(statusMachine.pending.value); // true
|
|
18
|
-
*
|
|
19
|
-
* // Завершение валидации с ошибками
|
|
20
|
-
* statusMachine.completeValidation(true);
|
|
21
|
-
* console.log(statusMachine.invalid.value); // true
|
|
22
|
-
*
|
|
23
|
-
* // Отключение поля
|
|
24
|
-
* statusMachine.disable();
|
|
25
|
-
* console.log(statusMachine.disabled.value); // true
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export declare class FormStatusMachine {
|
|
29
|
-
/** Внутренний сигнал статуса */
|
|
30
|
-
private readonly _status;
|
|
31
|
-
/** Публичный read-only сигнал статуса */
|
|
32
|
-
readonly status: ReadonlySignal<FieldStatus>;
|
|
33
|
-
/** Поле валидно */
|
|
34
|
-
readonly valid: ReadonlySignal<boolean>;
|
|
35
|
-
/** Поле невалидно */
|
|
36
|
-
readonly invalid: ReadonlySignal<boolean>;
|
|
37
|
-
/** Идет валидация */
|
|
38
|
-
readonly pending: ReadonlySignal<boolean>;
|
|
39
|
-
/** Поле отключено */
|
|
40
|
-
readonly disabled: ReadonlySignal<boolean>;
|
|
41
|
-
/**
|
|
42
|
-
* @param initial - Начальный статус (по умолчанию 'valid')
|
|
43
|
-
*/
|
|
44
|
-
constructor(initial?: FieldStatus);
|
|
45
|
-
/**
|
|
46
|
-
* Начать валидацию
|
|
47
|
-
*
|
|
48
|
-
* Переводит статус в 'pending' если поле не отключено
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```typescript
|
|
52
|
-
* statusMachine.startValidation();
|
|
53
|
-
* // status: 'pending'
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
startValidation(): void;
|
|
57
|
-
/**
|
|
58
|
-
* Завершить валидацию
|
|
59
|
-
*
|
|
60
|
-
* @param hasErrors - Есть ли ошибки валидации
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```typescript
|
|
64
|
-
* // Валидация успешна
|
|
65
|
-
* statusMachine.completeValidation(false);
|
|
66
|
-
* // status: 'valid'
|
|
67
|
-
*
|
|
68
|
-
* // Есть ошибки
|
|
69
|
-
* statusMachine.completeValidation(true);
|
|
70
|
-
* // status: 'invalid'
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
completeValidation(hasErrors: boolean): void;
|
|
74
|
-
/**
|
|
75
|
-
* Установить ошибки напрямую (без перехода через pending)
|
|
76
|
-
*
|
|
77
|
-
* Используется для синхронной валидации или установки ошибок извне
|
|
78
|
-
*
|
|
79
|
-
* @param hasErrors - Есть ли ошибки
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* ```typescript
|
|
83
|
-
* statusMachine.setErrors(true);
|
|
84
|
-
* // status: 'invalid'
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
setErrors(hasErrors: boolean): void;
|
|
88
|
-
/**
|
|
89
|
-
* Отключить поле
|
|
90
|
-
*
|
|
91
|
-
* Переводит статус в 'disabled'
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```typescript
|
|
95
|
-
* statusMachine.disable();
|
|
96
|
-
* // status: 'disabled'
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
disable(): void;
|
|
100
|
-
/**
|
|
101
|
-
* Включить поле
|
|
102
|
-
*
|
|
103
|
-
* @param hasErrors - Есть ли ошибки (определяет valid/invalid)
|
|
104
|
-
*
|
|
105
|
-
* @example
|
|
106
|
-
* ```typescript
|
|
107
|
-
* statusMachine.enable(false);
|
|
108
|
-
* // status: 'valid'
|
|
109
|
-
*
|
|
110
|
-
* statusMachine.enable(true);
|
|
111
|
-
* // status: 'invalid'
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
enable(hasErrors?: boolean): void;
|
|
115
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { ComponentType } from 'react';
|
|
2
|
-
import { Signal } from '@preact/signals-core';
|
|
3
|
-
import { ValidatorFn, AsyncValidatorFn, AnyFunction } from './index';
|
|
4
|
-
/**
|
|
5
|
-
* Конфигурация поля
|
|
6
|
-
* @group Types
|
|
7
|
-
* @category Configuration Types
|
|
8
|
-
* @remarks
|
|
9
|
-
* В схеме `createForm({ model, schema })` лист связывают через **`value: model.$.field`**: harvest
|
|
10
|
-
* ловит узел по идентичности `Signal` (`node.value instanceof Signal` — см. `create-form.ts`) и
|
|
11
|
-
* нормализует его в `valueSignal`. Писать `valueSignal:` **в самой схеме `createForm` нельзя** —
|
|
12
|
-
* harvest его не подхватит (проверка идёт по `node.value`), и `component`/`componentProps` этого
|
|
13
|
-
* узла молча отбросятся. `value`/`valueSignal` на прямом FieldNode-конфиге — это
|
|
14
|
-
* post-normalization / legacy-слой, а не форма записи в схеме `createForm`.
|
|
15
|
-
*/
|
|
16
|
-
export interface FieldConfig<T> {
|
|
17
|
-
/**
|
|
18
|
-
* Начальное значение-литерал (legacy-путь). Под архитектурой M1 значение приходит из
|
|
19
|
-
* {@link FieldConfig.valueSignal} (сигнал {@link FormModel}); тогда `value` не требуется.
|
|
20
|
-
*/
|
|
21
|
-
value?: T | null;
|
|
22
|
-
/**
|
|
23
|
-
* Сигнал значения из {@link FormModel} (M1). Если задан — служит источником истины значения
|
|
24
|
-
* поля (нода не владеет значением, а ссылается на этот сигнал). Имеет приоритет над `value`.
|
|
25
|
-
*/
|
|
26
|
-
valueSignal?: Signal<T>;
|
|
27
|
-
/**
|
|
28
|
-
* UI-компонент поля. Опционален: core-часть можно использовать без ссылки на компонент
|
|
29
|
-
* (значение/валидация работают без UI; компонент нужен только для рендеринга).
|
|
30
|
-
*/
|
|
31
|
-
component?: ComponentType<any>;
|
|
32
|
-
componentProps?: any;
|
|
33
|
-
validators?: ValidatorFn<T>[];
|
|
34
|
-
asyncValidators?: AsyncValidatorFn<T>[];
|
|
35
|
-
disabled?: boolean;
|
|
36
|
-
updateOn?: 'change' | 'blur' | 'submit';
|
|
37
|
-
/** Задержка (в мс) перед запуском асинхронной валидации */
|
|
38
|
-
debounce?: number;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* «Хотя бы одно поле обязано присутствовать»: превращает объект со всеми опциональными
|
|
42
|
-
* свойствами в объединение вариантов, где ровно один ключ обязателен, а остальные опциональны.
|
|
43
|
-
* Пустой объект `{}` не assignable ни к одному варианту и потому отклоняется.
|
|
44
|
-
*
|
|
45
|
-
* @internal
|
|
46
|
-
*/
|
|
47
|
-
type RequireAtLeastOne<T> = {
|
|
48
|
-
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
|
|
49
|
-
}[keyof T];
|
|
50
|
-
/**
|
|
51
|
-
* {@link FieldConfig}, из которого нельзя сделать пустой `{}`.
|
|
52
|
-
*
|
|
53
|
-
* Все свойства {@link FieldConfig} опциональны, поэтому в {@link FormSchema} опечатка вида
|
|
54
|
-
* `{ email: {} }` молча проходит проверку типов и в рантайме становится пустой подгруппой
|
|
55
|
-
* вместо поля. `NonEmptyFieldConfig` требует наличия хотя бы одного свойства (value/valueSignal/
|
|
56
|
-
* component/validators/…), оставляя каждое из них опциональным по отдельности.
|
|
57
|
-
*
|
|
58
|
-
* @internal
|
|
59
|
-
*/
|
|
60
|
-
type NonEmptyFieldConfig<T> = RequireAtLeastOne<FieldConfig<T>>;
|
|
61
|
-
/**
|
|
62
|
-
* **Data-shaped** конфиг формы: ключи повторяют структуру данных `T`, значения — {@link FieldConfig}
|
|
63
|
-
* (или вложенный `FormSchema`). Форма конфига, из которой строится {@link GroupNode}.
|
|
64
|
-
* - `T[] -> [FormSchema<T>]` (массив с одним элементом)
|
|
65
|
-
* - `object -> FormSchema<T>` (группа)
|
|
66
|
-
* - `primitive -> FieldConfig<T>` (поле)
|
|
67
|
-
*
|
|
68
|
-
* Использует NonNullable для корректной обработки опциональных полей.
|
|
69
|
-
*
|
|
70
|
-
* ⚠️ Не путать с {@link FormSchemaNode} — тот описывает **узел дерева** M1-схемы (лист/массив/
|
|
71
|
-
* контейнер), передаваемой в `createForm({ model, schema })`. `FormSchema` — это data-shaped конфиг
|
|
72
|
-
* (ключи = поля данных), а не узел дерева.
|
|
73
|
-
*
|
|
74
|
-
* @group Types
|
|
75
|
-
* @category Configuration Types
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```typescript
|
|
79
|
-
* interface Form {
|
|
80
|
-
* name: string; // → FieldConfig<string>
|
|
81
|
-
* address: { // → FormSchema<Address>
|
|
82
|
-
* city: string;
|
|
83
|
-
* street: string;
|
|
84
|
-
* };
|
|
85
|
-
* items?: Array<{ // → [FormSchema<Item>] (опциональный)
|
|
86
|
-
* title: string;
|
|
87
|
-
* price: number;
|
|
88
|
-
* }>;
|
|
89
|
-
* }
|
|
90
|
-
*
|
|
91
|
-
* const schema: FormSchema<Form> = {
|
|
92
|
-
* name: { value: '', component: Input },
|
|
93
|
-
* address: {
|
|
94
|
-
* city: { value: '', component: Input },
|
|
95
|
-
* street: { value: '', component: Input },
|
|
96
|
-
* },
|
|
97
|
-
* items: [{
|
|
98
|
-
* title: { value: '', component: Input },
|
|
99
|
-
* price: { value: 0, component: Input },
|
|
100
|
-
* }],
|
|
101
|
-
* };
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
export type FormSchema<T> = {
|
|
105
|
-
[K in keyof T]: NonNullable<T[K]> extends string | number | boolean ? NonEmptyFieldConfig<T[K]> : NonNullable<T[K]> extends Array<infer U> ? U extends string | number | boolean ? NonEmptyFieldConfig<T[K]> : U extends Date | File | Blob | AnyFunction ? NonEmptyFieldConfig<T[K]> : [FormSchema<U>] : NonNullable<T[K]> extends Date | File | Blob | AnyFunction ? NonEmptyFieldConfig<T[K]> : FormSchema<NonNullable<T[K]>>;
|
|
106
|
-
};
|
|
107
|
-
export {};
|