@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.
- package/README.md +90 -68
- package/dist/behaviors-value-HA_Q03hc.js +108 -0
- package/dist/behaviors.js +382 -217
- package/dist/create-model-BvcbNPgL.js +30 -0
- 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 +42 -8
- package/dist/form/behaviors/context.d.ts +37 -4
- package/dist/form/behaviors/index.d.ts +6 -4
- package/dist/form/behaviors/internals.d.ts +9 -3
- 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 +78 -0
- package/dist/form/behaviors/types.d.ts +25 -5
- package/dist/form/create-form.d.ts +57 -70
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +153 -0
- 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 +20 -20
- 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 +153 -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 +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 +147 -46
- package/dist/form/types/validation-schema.d.ts +23 -27
- package/dist/form/validation/context.d.ts +46 -6
- package/dist/form/validation/index.d.ts +9 -8
- package/dist/form/validation/operators.d.ts +71 -17
- 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 -19
- 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 +958 -2798
- 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 +16 -5
- package/dist/model/model-nodes.d.ts +138 -8
- package/dist/model/model-signals-proxy.d.ts +11 -5
- package/dist/model/model-value-proxy.d.ts +84 -3
- package/dist/model/safe-effect.d.ts +33 -1
- package/dist/model/types.d.ts +70 -17
- package/dist/model.js +21 -17
- 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 +27 -0
- 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 +7 -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 -8
- 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 +131 -83
- package/docs/llms/10-arrays.md +119 -69
- 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 +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 +276 -128
- package/docs/llms/29-async-preload.md +40 -30
- 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 +69 -43
- package/docs/llms/35-unified-contract.md +307 -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 +397 -0
- package/llms-index.json +1 -1
- package/llms.txt +6968 -3278
- package/package.json +23 -4
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/create-model-B8U-COv-.js +0 -103
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/form/create-core-form.d.ts +0 -70
- 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 -121
- 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-signals-proxy-2vqS243J.js +0 -250
- package/dist/node-DGEFMTQu.js +0 -18
- package/dist/strategy-BXS_gBfz.js +0 -170
- package/dist/validation-error-B-U3RsV0.js +0 -11
package/dist/validators.js
CHANGED
|
@@ -7,8 +7,8 @@ import { email as w } from "./validators/email.js";
|
|
|
7
7
|
import { pattern as k } from "./validators/pattern.js";
|
|
8
8
|
import { url as B } from "./validators/url.js";
|
|
9
9
|
import { phone as O } from "./validators/phone.js";
|
|
10
|
-
import { isNumber as
|
|
11
|
-
import { integer as
|
|
10
|
+
import { isNumber as E } from "./validators/is-number.js";
|
|
11
|
+
import { integer as H } from "./validators/integer.js";
|
|
12
12
|
import { multipleOf as K } from "./validators/multiple-of.js";
|
|
13
13
|
import { nonNegative as P } from "./validators/non-negative.js";
|
|
14
14
|
import { nonZero as R } from "./validators/non-zero.js";
|
|
@@ -19,7 +19,7 @@ import { pastDate as ee } from "./validators/past-date.js";
|
|
|
19
19
|
import { futureDate as ne } from "./validators/future-date.js";
|
|
20
20
|
import { minAge as ie } from "./validators/min-age.js";
|
|
21
21
|
import { maxAge as le } from "./validators/max-age.js";
|
|
22
|
-
import { v as l } from "./validation-
|
|
22
|
+
import { v as l } from "./validation-issue-B9mjI8jz.js";
|
|
23
23
|
function s(r) {
|
|
24
24
|
if (typeof r != "object" || r === null) return !1;
|
|
25
25
|
const t = r;
|
|
@@ -32,7 +32,7 @@ function m(r, t) {
|
|
|
32
32
|
const e = t.split(",").map((o) => o.trim().toLowerCase()).filter(Boolean);
|
|
33
33
|
if (e.length === 0) return !0;
|
|
34
34
|
const n = r.name.toLowerCase(), i = (r.type ?? "").toLowerCase();
|
|
35
|
-
return e.some((o) => o.startsWith(".") ? n.endsWith(o) : o.endsWith("/*") ? i.startsWith(o.slice(0, -1)) : o.includes("/") ? i === o : !1);
|
|
35
|
+
return e.some((o) => o.startsWith(".") ? n.endsWith(o) : o === "*/*" ? !0 : o.endsWith("/*") ? i.startsWith(o.slice(0, -1)) : o.includes("/") ? i === o : !1);
|
|
36
36
|
}
|
|
37
37
|
function c(r, t) {
|
|
38
38
|
return (e) => {
|
|
@@ -50,7 +50,7 @@ function c(r, t) {
|
|
|
50
50
|
return null;
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
function
|
|
53
|
+
function x(r, t) {
|
|
54
54
|
return (e) => {
|
|
55
55
|
if (e == null || e === "")
|
|
56
56
|
return null;
|
|
@@ -66,7 +66,7 @@ function a(r, t) {
|
|
|
66
66
|
return null;
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
function
|
|
69
|
+
function a(r, t) {
|
|
70
70
|
return (e) => {
|
|
71
71
|
if (e == null || e === "")
|
|
72
72
|
return null;
|
|
@@ -78,7 +78,7 @@ function x(r, t) {
|
|
|
78
78
|
return null;
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
function
|
|
81
|
+
function z(r, t) {
|
|
82
82
|
return (e) => {
|
|
83
83
|
if (e == null || e === "")
|
|
84
84
|
return null;
|
|
@@ -86,15 +86,15 @@ function g(r, t) {
|
|
|
86
86
|
return n === null || n.length === 0 ? null : n.length > r ? l("maxFiles", t, { maxFiles: r, actualCount: n.length }) : null;
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
-
function
|
|
89
|
+
function g(r, t) {
|
|
90
90
|
return (e) => {
|
|
91
91
|
if (e == null || e === "")
|
|
92
92
|
return null;
|
|
93
93
|
const n = f(e);
|
|
94
|
-
return n === null
|
|
94
|
+
return n === null ? null : n.length < r ? l("minFiles", t, { minFiles: r, actualCount: n.length }) : null;
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
-
function
|
|
97
|
+
function h(r, t) {
|
|
98
98
|
return (e) => {
|
|
99
99
|
if (e == null || e === "")
|
|
100
100
|
return null;
|
|
@@ -112,25 +112,25 @@ function z(r, t) {
|
|
|
112
112
|
}
|
|
113
113
|
export {
|
|
114
114
|
w as email,
|
|
115
|
-
|
|
115
|
+
a as fileType,
|
|
116
116
|
ne as futureDate,
|
|
117
|
-
|
|
117
|
+
H as integer,
|
|
118
118
|
V as isDate,
|
|
119
119
|
s as isFileLike,
|
|
120
|
-
|
|
120
|
+
E as isNumber,
|
|
121
121
|
m as matchesFileAccept,
|
|
122
122
|
L as max,
|
|
123
123
|
le as maxAge,
|
|
124
124
|
$ as maxDate,
|
|
125
125
|
c as maxFileSize,
|
|
126
|
-
|
|
126
|
+
z as maxFiles,
|
|
127
127
|
T as maxLength,
|
|
128
|
-
|
|
128
|
+
h as maxTotalFileSize,
|
|
129
129
|
b as min,
|
|
130
130
|
ie as minAge,
|
|
131
131
|
Y as minDate,
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
x as minFileSize,
|
|
133
|
+
g as minFiles,
|
|
134
134
|
D as minLength,
|
|
135
135
|
K as multipleOf,
|
|
136
136
|
P as nonNegative,
|
|
@@ -2,52 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
### Imports (CRITICALLY IMPORTANT)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
| What | Where
|
|
8
|
-
| ------------------------------------------------------------------------------------------------------ |
|
|
9
|
-
| `createModel`, `createForm`
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
5
|
+
Значения живут в **модели** (`createModel`), а форма (`createForm`) строит ноды поверх сигналов модели. Схема, правила и поведение привязываются к ручкам модели (`model.$.field`), а не к строковым путям.
|
|
6
|
+
|
|
7
|
+
| What | Where |
|
|
8
|
+
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
|
|
9
|
+
| `createModel`, `createForm`, `createFormFromModel`, `disposeForm`, `arrayOf`, `modelOf` | `@reformer/core` |
|
|
10
|
+
| `compileSchema`, `createFormFlow`, `createFormLevel`, `createSubmission`, `getNodeForSignal` | `@reformer/core` |
|
|
11
|
+
| `isFormNode`, `isFormControl`, `isFormGroup`, `isFormArray` (гарды нод) | `@reformer/core` |
|
|
12
|
+
| `FormModel`, `FormBundle`, `FormNode`, `FormControl`, `FormGroup`, `FormArray`, `FormFields` | `@reformer/core` |
|
|
13
|
+
| `ModelSignals`, `ModelArray`, `ModelValue`, `ModelObject`, `PathAwareSignal` | `@reformer/core` |
|
|
14
|
+
| `ValidationIssue`, `RuleOptions`, `FormSchemaNode`, `ComponentRef`, `CompiledSchema` | `@reformer/core` |
|
|
15
|
+
| `FormValidation`, `FormFlowConfig`, `FormFlow`, `FlowTransition`, `SubmitResult` | `@reformer/core` |
|
|
16
|
+
| `FormSubmission`, `SubmissionDependencies`, `ActionCheck` (отправка) | `@reformer/core` |
|
|
17
|
+
| `defineValidation`, `validate`, `validateAsync`, `validateWhen`, `apply`, `applyEach` | `@reformer/core/validation` |
|
|
18
|
+
| `validateModel`, `runValidation`, `applyValidationResult` (раннер), `createFormValidation` | `@reformer/core/validation` |
|
|
19
|
+
| `Rule`, `AsyncRule`, `ValidationDefinition`, `ValidationScope`, `ValidationResult` (типы) | `@reformer/core/validation` |
|
|
20
|
+
| `FormValidationController`, `FormValidationRunOptions`, `ValidationStrategyKind` (типы стратегии) | `@reformer/core/validation` |
|
|
21
|
+
| `useFormBundle`, `useFormControl`, `useFormControlValue`, `useArrayLength`, `useFormValidation` | `@reformer/core/react` |
|
|
22
|
+
| `useFormFlow`, `FormBundleContext`, `useFormBundleContext` | `@reformer/core/react` |
|
|
23
|
+
| `bindFieldProps`, `getFieldAdapter`, `FieldAdapter`, `FieldHandle`, `FieldControlState` (связка поля) | `@reformer/core/react` |
|
|
24
|
+
| `I18nProvider`, `useI18n`, `useMessages`, `useIssueMessage` | `@reformer/core/react` |
|
|
25
|
+
| `defineIssueMessages`, `resolveIssueMessage`, `createI18n`, `extendLocale`, `createLocaleLoader` | `@reformer/core/i18n` |
|
|
26
|
+
| `computeFrom`, `copyFrom`, `watchField`, `transformValue`, `resetWhen`, `syncFields`, `revalidateWhen` | `@reformer/core/model` (примитивы над сигналами) |
|
|
27
|
+
| `deepEqual`, `deferredWritesSettled` (сравнение значений, ожидание отложенных записей поведения) | `@reformer/core/model` |
|
|
28
|
+
| `required`, `min`, `max`, `minLength`, `maxLength`, `email`, `pattern`, `url`, `phone` | `@reformer/core/validators` |
|
|
29
|
+
| `isNumber`, `integer`, `multipleOf`, `nonNegative`, `nonZero` | `@reformer/core/validators` |
|
|
30
|
+
| `isDate`, `minDate`, `maxDate`, `pastDate`, `futureDate`, `minAge`, `maxAge` | `@reformer/core/validators` |
|
|
31
|
+
| `defineFormBehavior`, `compute`, `computeFrom`, `copyFrom`, `onChange`, `enableWhen`, `disableWhen` | `@reformer/core/behaviors` |
|
|
32
|
+
| `transformValue`, `resetWhen`, `syncFields`, `revalidateWhen`, `apply`, `applyEach`, `aggregateInto` | `@reformer/core/behaviors` |
|
|
33
|
+
| `exclusiveFlag`, `onDispose`, `getScope`, `effect`, `defer` | `@reformer/core/behaviors` |
|
|
34
|
+
| `hideWhen`, `onComponentEvent`, `renderEffect`, `onInit`, `onMount`, `onUnmount` (узлы схемы) | `@reformer/core/behaviors` |
|
|
35
|
+
|
|
36
|
+
> **Корень `@reformer/core` без React.** Хуки, контекст сборки, связка поля с контролом и
|
|
37
|
+
> провайдер локали — только из `@reformer/core/react`. Корень работает на сервере и с любым слоем
|
|
38
|
+
> представления.
|
|
39
|
+
|
|
40
|
+
> **Поведение формы — `@reformer/core/behaviors`.** Декларативный DSL (`defineFormBehavior` +
|
|
41
|
+
> операторы) регистрирует cleanup сам и передаётся в `createForm({ behavior })`. Примитивы над
|
|
42
|
+
> сигналами (`computeFrom`, `copyFrom`, `watchField`, …) в корень `@reformer/core` не входят: они
|
|
43
|
+
> лежат в сабпате `@reformer/core/model`, принимают **сигналы** (`model.$.x`), возвращают
|
|
44
|
+
> **cleanup-функцию** и нужны вне формы. `enableWhen` / `disableWhen` существуют только как
|
|
45
|
+
> операторы поведения. См. `20-compute-vs-watch.md`.
|
|
46
|
+
|
|
47
|
+
> **Валидация — отдельный слой.** Схема формы правил НЕ несёт. Правила живут в
|
|
48
|
+
> `defineValidation<T>(({ model, cross }) => { validate(model.$.x, [required(), min(50000)]); ... })`
|
|
33
49
|
> из `@reformer/core/validation`; фабрики `required()`, `min(50000)`, `email()` возвращают
|
|
34
|
-
> `Rule<T> = (value) =>
|
|
35
|
-
>
|
|
36
|
-
>
|
|
50
|
+
> `Rule<T> = (value: T) => ValidationIssue | null` и передаются массивом в `validate(sig, [...])`.
|
|
51
|
+
> Правила отдают сборке: `createForm({ validation, flow })`. Запуск — уровнем формы:
|
|
52
|
+
> `bundle.validate()` (`ValidationResult` со статусом `valid` / `invalid` / `error` / `cancelled`),
|
|
53
|
+
> `bundle.validateStep(id)`, отправка `bundle.submission.submit(handler)` (`SubmitResult`). Без
|
|
54
|
+
> сборки — раннер `validateModel(model, rules)` (`Promise<boolean>`) либо `runValidation` (полный
|
|
55
|
+
> результат). Снять сборку — `bundle.dispose()`. См. `38-form-flow.md`.
|
|
56
|
+
|
|
57
|
+
> **Текста у замечания нет.** `ValidationIssue = { code, params?, severity? }`; строку по коду
|
|
58
|
+
> даёт словарь формы (`defineIssueMessages`) поверх словаря локали. Опции правил —
|
|
59
|
+
> `RuleOptions = { code?, params? }`. См. `36-issue-messages.md`.
|
|
37
60
|
|
|
38
61
|
### Type Values
|
|
39
62
|
|
|
40
63
|
- Опциональные числа: `number | null` (конвенция «пользователь очистил поле»)
|
|
41
64
|
- Опциональные строки: `string` (по умолчанию пустая строка) или `string | null`
|
|
42
|
-
-
|
|
65
|
+
- Форму данных объявляй через `interface` или `type` — оба варианта работают, см.
|
|
66
|
+
`30-type-safety-recipes.md`
|
|
43
67
|
|
|
44
68
|
### React Hooks Comparison (CRITICALLY IMPORTANT)
|
|
45
69
|
|
|
46
70
|
| Hook | Return Type | Subscribes To | Use Case |
|
|
47
71
|
|------|-------------|---------------|----------|
|
|
48
|
-
| `useFormControl(field)` | `{ value,
|
|
72
|
+
| `useFormControl(field)` | `{ value, issues, disabled, touched, dirty, valid, invalid, pending, shouldShowError, shouldShowIssues, componentProps }` | Все сигналы поля | Полное состояние поля, инпуты |
|
|
49
73
|
| `useFormControlValue(field)` | `T` (значение напрямую) | Только сигнал value | Условный рендеринг |
|
|
50
74
|
| `useArrayLength(array)` | `number` | Только длина массива | Реактивная длина массива |
|
|
75
|
+
| `useFormFlow(bundle.flow)` | `{ current, index, completed, isFirst, isLast, validating }` | Сигналы потока шагов | Свои кнопки и индикатор шагов |
|
|
76
|
+
| `useIssueMessage(messages?)` | `(issue) => string` | Локаль | Текст замечания в своём компоненте |
|
|
51
77
|
|
|
52
78
|
**CRITICAL**: Не деструктурируй `useFormControlValue`! Он возвращает `T` напрямую, НЕ `{ value: T }`.
|
|
53
79
|
|
|
@@ -59,5 +85,5 @@ const { value: loanType } = useFormControlValue(control.loanType);
|
|
|
59
85
|
const loanType = useFormControlValue(control.loanType);
|
|
60
86
|
|
|
61
87
|
// CORRECT - useFormControl returns object, destructuring OK
|
|
62
|
-
const { value,
|
|
88
|
+
const { value, issues, disabled } = useFormControl(control.loanType);
|
|
63
89
|
```
|
|
@@ -1,87 +1,99 @@
|
|
|
1
1
|
## 1.5 QUICK START - Minimal Working Form
|
|
2
2
|
|
|
3
3
|
> **Schema-driven UI rule (read first)**: компонент И его пропсы (label, placeholder,
|
|
4
|
-
> options, type) объявляются в
|
|
4
|
+
> options, type) объявляются в **узле схемы** (`component` + `componentProps`).
|
|
5
5
|
> В JSX рендерится один универсальный `<FormField control={form.x} />` из
|
|
6
6
|
> `@reformer/ui-kit` БЕЗ дополнительных props. Не пиши свои `Input`/`Select`/
|
|
7
7
|
> `Checkbox`-обёртки с `label`-prop'ами — это anti-pattern. См.
|
|
8
8
|
> `find_recipe(package="@reformer/ui-kit", topic="form-field-integration")`.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
(`model.$.field`)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
**Модель данных** — источник истины значений. Схема — дерево узлов, привязанных к ручкам модели
|
|
11
|
+
(`model: model.$.field`). Сборка идёт ОДНИМ вызовом `createForm`: он создаёт модель, строит форму,
|
|
12
|
+
запускает поведение и собирает уровень формы — проверку и отправку. Правил в схеме нет — они
|
|
13
|
+
живут в отдельной схеме `defineValidation` из `@reformer/core/validation` и уходят в сборку полем
|
|
14
|
+
`validation`; запускает их `bundle.submission.submit(handler)` / `bundle.validate()`. Текста у
|
|
15
|
+
правил нет: правило несёт код, строку по коду даёт словарь формы (`defineIssueMessages`).
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { createForm, type FormModel, type FormGroup, type FormSchemaNode } from '@reformer/core';
|
|
19
|
+
import { useFormBundle } from '@reformer/core/react';
|
|
20
|
+
import { defineIssueMessages } from '@reformer/core/i18n';
|
|
21
|
+
import { defineValidation, validate } from '@reformer/core/validation';
|
|
20
22
|
import { required, email } from '@reformer/core/validators';
|
|
23
|
+
import { ValidationMessagesProvider } from '@reformer/cdk';
|
|
21
24
|
import { FormField, Input, Button } from '@reformer/ui-kit';
|
|
22
25
|
|
|
23
|
-
// 1.
|
|
24
|
-
|
|
26
|
+
// 1. Form data type — `interface` or `type`, both work
|
|
27
|
+
interface ContactForm {
|
|
25
28
|
name: string;
|
|
26
29
|
email: string;
|
|
27
|
-
}
|
|
30
|
+
}
|
|
28
31
|
|
|
29
|
-
// 2.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
32
|
+
// 2. Схема — билдер (model) => дерево узлов. Узел поля: привязка `model` + component/componentProps.
|
|
33
|
+
// Вложенные узлы лежат в `children`. Правил здесь нет — они в отдельной схеме (шаг 3).
|
|
34
|
+
const contactSchema = (model: FormModel<ContactForm>): FormSchemaNode => ({
|
|
35
|
+
children: [
|
|
36
|
+
{
|
|
37
|
+
model: model.$.name,
|
|
38
|
+
component: Input,
|
|
39
|
+
componentProps: { label: 'Name', placeholder: 'Your name' },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
model: model.$.email,
|
|
43
|
+
component: Input,
|
|
44
|
+
componentProps: { label: 'Email', type: 'email' },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
46
48
|
|
|
47
|
-
//
|
|
48
|
-
const contactValidation =
|
|
49
|
-
validate(model.$.name, [required({
|
|
50
|
-
validate(model.$.email, [required({
|
|
49
|
+
// 3. Схема валидации — отдельный слой (@reformer/core/validation). Правило несёт код, не текст.
|
|
50
|
+
const contactValidation = defineValidation<ContactForm>(({ model }) => {
|
|
51
|
+
validate(model.$.name, [required({ code: 'name.required' })]);
|
|
52
|
+
validate(model.$.email, [required({ code: 'email.required' }), email()]);
|
|
51
53
|
});
|
|
52
54
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
schema: buildSchema, // билдер (model) => tree
|
|
58
|
-
validation: contactValidation,
|
|
55
|
+
// 4. Тексты замечаний по кодам — словарь формы (встроенный код `email` берёт текст из локали)
|
|
56
|
+
const contactMessages = defineIssueMessages({
|
|
57
|
+
'name.required': 'Укажите имя',
|
|
58
|
+
'email.required': 'Укажите email',
|
|
59
59
|
});
|
|
60
|
-
|
|
60
|
+
|
|
61
|
+
// 5. Сборка ОДНИМ вызовом: модель из initial + ноды поверх её сигналов + уровень формы.
|
|
62
|
+
const createContactForm = () =>
|
|
63
|
+
createForm<ContactForm>({
|
|
64
|
+
initial: { name: '', email: '' },
|
|
65
|
+
schema: contactSchema,
|
|
66
|
+
validation: contactValidation,
|
|
67
|
+
});
|
|
61
68
|
|
|
62
69
|
// 6. Use in React component — thin JSX, FormField does ALL heavy lifting
|
|
63
70
|
function ContactFormComponent() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
// useFormBundle зовёт фабрику ровно один раз и держит сборку стабильной между рендерами.
|
|
72
|
+
const contact = useFormBundle(createContactForm);
|
|
73
|
+
const { form } = contact;
|
|
74
|
+
|
|
75
|
+
const handleSubmit = async (event: React.FormEvent) => {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
// Один прогон всех правил: при замечаниях обработчик не вызывается, а вся форма помечается
|
|
78
|
+
// тронутой — FormField покажет замечания.
|
|
79
|
+
const outcome = await contact.submission.submit((values) => api.send(values));
|
|
80
|
+
if (outcome.status === 'submitted') console.log('Form submitted');
|
|
71
81
|
};
|
|
72
82
|
|
|
73
83
|
return (
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
<ValidationMessagesProvider messages={contactMessages}>
|
|
85
|
+
<form onSubmit={handleSubmit}>
|
|
86
|
+
<FormField control={form.name} testId="name" />
|
|
87
|
+
<FormField control={form.email} testId="email" />
|
|
88
|
+
<Button type="submit">Send</Button>
|
|
89
|
+
</form>
|
|
90
|
+
</ValidationMessagesProvider>
|
|
79
91
|
);
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
// 7. Pass form to child components via props (NOT context!)
|
|
83
95
|
type FormStepProps = {
|
|
84
|
-
form:
|
|
96
|
+
form: FormGroup<ContactForm>;
|
|
85
97
|
};
|
|
86
98
|
|
|
87
99
|
function FormStep({ form }: FormStepProps) {
|
|
@@ -89,16 +101,27 @@ function FormStep({ form }: FormStepProps) {
|
|
|
89
101
|
}
|
|
90
102
|
```
|
|
91
103
|
|
|
92
|
-
> **Стабильность
|
|
93
|
-
>
|
|
104
|
+
> **Стабильность сборки.** Форму создают ОДИН раз: в React — `useFormBundle(фабрика)`, вне React —
|
|
105
|
+
> константой модуля. Иначе форма пересоздаётся на каждый рендер. См. `28-submit-and-reset.md`,
|
|
106
|
+
> `29-async-preload.md`.
|
|
94
107
|
|
|
95
|
-
|
|
108
|
+
> **Проверка и отправка — уровень формы.** Отправка — отдельный объект сборки:
|
|
109
|
+
> `bundle.submission.submit(handler)` прогоняет все правила один раз и зовёт обработчик только при
|
|
110
|
+
> статусе `valid`; результат — `SubmitResult` (`submitted` / `invalid` / `error` / `cancelled` /
|
|
111
|
+
> `failed`). `bundle.validate()` проверяет всю форму и отвечает `ValidationResult`. У нод методов
|
|
112
|
+
> `validate()` / `submit()` нет. Многошаговая форма — `createForm({ flow: { steps: [{ id, rules }] } })`,
|
|
113
|
+
> см. `38-form-flow.md`. Вне React сборку снимают `bundle.dispose()`, когда форма больше не нужна.
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
### Arrays of objects — узел `{ model, item }`
|
|
116
|
+
|
|
117
|
+
Массивы объектов принадлежат модели (`model.arrayField`). В схеме массив под-форм объявляется узлом
|
|
118
|
+
`{ model: model.$.<path>, item: (itemModel) => узел }`, где `item` строит разметку одной строки из
|
|
119
|
+
её под-модели (`FormModel<Item>`):
|
|
100
120
|
|
|
101
121
|
```typescript
|
|
122
|
+
import { arrayOf, createForm, type FormModel, type FormSchemaNode } from '@reformer/core';
|
|
123
|
+
import { FieldArray, InputNumber, SelectAsync, Textarea } from '@reformer/ui-kit';
|
|
124
|
+
|
|
102
125
|
type PropertyItem = {
|
|
103
126
|
type: 'apartment' | 'house';
|
|
104
127
|
description: string;
|
|
@@ -107,33 +130,41 @@ type PropertyItem = {
|
|
|
107
130
|
|
|
108
131
|
type MyForm = { properties: PropertyItem[] };
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
// шаблон новой строки — его кладёт `push()` без значения и кнопка «Добавить»
|
|
134
|
+
const blankProperty = (): PropertyItem => ({ type: 'apartment', description: '', estimatedValue: 0 });
|
|
135
|
+
|
|
136
|
+
// разметка одной строки: item.$.field — ручка под-модели строки
|
|
137
|
+
const propertyRow = (item: FormModel<PropertyItem>): FormSchemaNode => ({
|
|
138
|
+
children: [
|
|
139
|
+
{
|
|
140
|
+
model: item.$.type,
|
|
141
|
+
component: SelectAsync,
|
|
142
|
+
componentProps: { label: 'Тип', options: [/* ... */] },
|
|
143
|
+
},
|
|
144
|
+
{ model: item.$.description, component: Textarea, componentProps: { label: 'Описание' } },
|
|
145
|
+
{
|
|
146
|
+
model: item.$.estimatedValue,
|
|
147
|
+
component: InputNumber,
|
|
148
|
+
componentProps: { label: 'Стоимость' },
|
|
149
|
+
},
|
|
150
|
+
],
|
|
125
151
|
});
|
|
126
152
|
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
153
|
+
const { model, form } = createForm<MyForm>({
|
|
154
|
+
initial: { properties: arrayOf(blankProperty) },
|
|
155
|
+
schema: (model) => ({
|
|
156
|
+
children: [{ model: model.$.properties, component: FieldArray, item: propertyRow }],
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
132
159
|
|
|
133
160
|
// Операции над массивом — на модели:
|
|
134
|
-
model.properties.push(
|
|
161
|
+
model.properties.push(); // новая строка по шаблону
|
|
162
|
+
model.properties.push({ type: 'house', description: '', estimatedValue: 0 });
|
|
135
163
|
model.properties.removeAt(0);
|
|
136
164
|
model.properties.length; // реактивная длина
|
|
165
|
+
form.properties.at(0); // форма строки (FormGroup<PropertyItem>)
|
|
166
|
+
form.properties.length; // число строк (число, не сигнал)
|
|
167
|
+
form.properties.keyAt(0); // стабильный ключ строки — для React `key`
|
|
137
168
|
```
|
|
138
169
|
|
|
139
170
|
Подробнее в `10-arrays.md`, `21-array-operations.md` и `find_recipe(topic="form-array")`.
|