@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
|
@@ -39,11 +39,11 @@ const behavior = defineFormBehavior<MyForm>(({ model, form }) => {
|
|
|
39
39
|
|
|
40
40
|
### Низкоуровневый примитив watchField
|
|
41
41
|
|
|
42
|
-
`watchField` из `@reformer/core` — базовая подписка на изменение сигнала (без debounce и
|
|
42
|
+
`watchField` из `@reformer/core/model` — базовая подписка на изменение сигнала (без debounce и
|
|
43
43
|
AbortSignal). `onChange` построен поверх него. Для простых синхронных реакций:
|
|
44
44
|
|
|
45
45
|
```typescript
|
|
46
|
-
import { watchField } from '@reformer/core';
|
|
46
|
+
import { watchField } from '@reformer/core/model';
|
|
47
47
|
|
|
48
48
|
// вызывается при каждом изменении (по умолчанию НЕ на инициализации)
|
|
49
49
|
const stop = watchField(model.$.country, (country) => {
|
|
@@ -1,91 +1,162 @@
|
|
|
1
1
|
## 12. MULTI-STEP FORM VALIDATION
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Многошаговая форма — **поток шагов** сборки: `createForm({ flow: { steps: [{ id, rules }] } })`.
|
|
4
|
+
Каждый шаг — идентификатор и своя `ValidationDefinition<Form>` (обычная функция
|
|
5
|
+
`({ model, cross }) => void`, обёрнутая `defineValidation`). Идентификатор шага — `selector` узла
|
|
6
|
+
шага в схеме. Правила вне шагов (межшаговые) — `validation: { extras }`: они проверяются только
|
|
7
|
+
целиком, при отправке. Валидация живёт ОТДЕЛЬНО от схемы формы: узел схемы (TS или JSON) правил не
|
|
8
|
+
несёт.
|
|
9
|
+
|
|
10
|
+
Сборка отдаёт поток `bundle.flow` (`next` / `previous` / `goTo`), проверку шага
|
|
11
|
+
`bundle.validateStep(id)`, полную проверку `bundle.validate()` и отправку
|
|
12
|
+
`bundle.submission.submit(handler)`. Замечания разносятся по нодам сами — UI подсветит поля
|
|
13
|
+
текущего шага. Полный справочник — [38-form-flow.md](./38-form-flow.md).
|
|
14
|
+
|
|
15
|
+
### Поток шагов сборки
|
|
9
16
|
|
|
10
17
|
```typescript
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
validate,
|
|
14
|
-
apply,
|
|
15
|
-
defineValidationSchema,
|
|
16
|
-
validateModel,
|
|
17
|
-
type ValidationSchema,
|
|
18
|
-
} from '@reformer/core/validation';
|
|
19
|
-
import { required, min } from '@reformer/core/validators';
|
|
18
|
+
import { createForm } from '@reformer/core';
|
|
19
|
+
import { useFormBundle } from '@reformer/core/react';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
21
|
+
const createLoanForm = () =>
|
|
22
|
+
createForm<Form>({
|
|
23
|
+
initial,
|
|
24
|
+
schema: formSchema, // узлы шагов несут selector: 'loan', 'applicant', 'confirm'
|
|
25
|
+
validation: { extras: crossStepRules }, // правила всей формы — только целиком
|
|
26
|
+
flow: {
|
|
27
|
+
steps: [
|
|
28
|
+
{ id: 'loan', rules: loanRules },
|
|
29
|
+
{ id: 'applicant', rules: applicantRules },
|
|
30
|
+
{ id: 'confirm' }, // шаг без правил
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
});
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
validate(model.$.personalData.lastName, [required()]);
|
|
30
|
-
});
|
|
35
|
+
// в компоненте: фабрика вызывается один раз, сборка стабильна между рендерами
|
|
36
|
+
const loan = useFormBundle(createLoanForm);
|
|
31
37
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
// в обработчиках событий
|
|
39
|
+
await loan.flow?.next(); // проверить правила текущего шага и перейти → FlowTransition
|
|
40
|
+
await loan.validateStep('loan'); // правила одного шага → ValidationResult
|
|
41
|
+
await loan.validate(); // все шаги + extras → ValidationResult
|
|
42
|
+
await loan.submission.submit(sendLoan); // один прогон всех правил; обработчик — только при valid
|
|
43
|
+
loan.validating.value; // идёт ли проверка — полная либо шага
|
|
35
44
|
```
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (ok) {
|
|
50
|
-
await onSubmit(model.get());
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
```
|
|
46
|
+
- Полный набор правил — правила всех шагов и `extras`; свой полный набор задаёт
|
|
47
|
+
`validation: { rules }` (тогда правила шагов работают только для `validateStep` / `next`).
|
|
48
|
+
- `next()` на шаге с замечаниями остаётся на месте: `{ status: 'stayed', reason: 'invalid',
|
|
49
|
+
validation }`; поля шага помечаются тронутыми. Переход — `{ status: 'moved', from, to }`.
|
|
50
|
+
- Поле правили, пока шла проверка шага (async-правило), — `next()` остаётся на шаге с
|
|
51
|
+
`reason: 'cancelled'`: правила этого значения не видели. Следующий `next()` проверит новое.
|
|
52
|
+
- Стратегия `afterFirstSubmit` переходит в живую фазу только от отправки
|
|
53
|
+
(`bundle.submission.submit`): `next()`, `validateStep` и `validate` её не переключают.
|
|
54
|
+
- `goTo(id)` открывает первый шаг и шаги после пройденных; прыжок вперёд через непройденный шаг —
|
|
55
|
+
`reason: 'locked'`. Назад — всегда.
|
|
56
|
+
- В dev сборка предупреждает о шаге `flow.steps`, для которого в схеме нет узла с таким `selector`.
|
|
57
|
+
- Правила (`loanRules`, `extras`) — стабильные ссылки: константы модуля через `defineValidation`.
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
В большой форме шаг объявляют один раз — в списке шагов (`flow/`): из него строятся и узлы шагов
|
|
60
|
+
схемы, и `flow.steps` сборки. См. `15-project-structure.md`.
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
### Визард — адаптер потока
|
|
63
|
+
|
|
64
|
+
`FormWizard` из `@reformer/cdk` и `@reformer/ui-kit` рисует поток и зовёт его переходы:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { FormWizard } from '@reformer/ui-kit';
|
|
68
|
+
|
|
69
|
+
function LoanWizard() {
|
|
70
|
+
const loan = useFormBundle(createLoanForm);
|
|
71
|
+
const send = async () => {
|
|
72
|
+
const outcome = await loan.submission.submit(sendLoan);
|
|
73
|
+
if (outcome.status !== 'submitted') showHint('Проверьте форму');
|
|
62
74
|
};
|
|
75
|
+
return <FormWizard form={loan.form} flow={loan.flow} steps={STEPS} onSubmit={send} />;
|
|
63
76
|
}
|
|
64
77
|
```
|
|
65
78
|
|
|
66
|
-
|
|
79
|
+
- В JSX — `form`, `flow={bundle.flow}` и `steps` пропсами; отправку проверяет приложение через
|
|
80
|
+
`bundle.submission.submit(handler)`.
|
|
81
|
+
- В схеме формы — узел `{ selector: 'wizard', component: FormWizard, children: [шаги] }`, шаг —
|
|
82
|
+
`{ selector: 'loan', component: Step, componentProps: { title }, children: [...] }`. Внутри
|
|
83
|
+
рендерера визард берёт форму и поток из сборки сам, а его `onSubmit` зовётся через
|
|
84
|
+
`bundle.submission.submit` — только после успешной проверки всей формы.
|
|
85
|
+
- Без `flow` и без сборки в контексте визард строит позиционный поток **без правил**.
|
|
86
|
+
- Навигация снаружи визарда — через сам поток: `bundle.flow?.goTo('loan')`; в поведении — `flow` из
|
|
87
|
+
области (`defineFormBehavior(({ model, flow }) => …)`).
|
|
88
|
+
|
|
89
|
+
### Шаги без визарда — `useFormFlow`
|
|
67
90
|
|
|
68
91
|
```tsx
|
|
69
|
-
|
|
70
|
-
const [step, setStep] = useState(1);
|
|
92
|
+
import { useFormFlow } from '@reformer/core/react';
|
|
71
93
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
};
|
|
94
|
+
function MultiStepForm() {
|
|
95
|
+
const loan = useFormBundle(createLoanForm);
|
|
96
|
+
const flow = loan.flow!;
|
|
97
|
+
const { current, isFirst, isLast, validating } = useFormFlow(flow);
|
|
76
98
|
|
|
77
99
|
return (
|
|
78
100
|
<div>
|
|
79
|
-
{
|
|
80
|
-
{
|
|
101
|
+
{current === 'loan' && <LoanFields form={loan.form} />}
|
|
102
|
+
{current === 'applicant' && <ApplicantFields form={loan.form} />}
|
|
103
|
+
{current === 'confirm' && <Summary form={loan.form} />}
|
|
81
104
|
|
|
82
|
-
<button onClick={() =>
|
|
105
|
+
<button onClick={() => flow.previous()} disabled={isFirst}>
|
|
83
106
|
Back
|
|
84
107
|
</button>
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
|
|
108
|
+
{isLast ? (
|
|
109
|
+
<button onClick={() => void loan.submission.submit(sendLoan)}>Submit</button>
|
|
110
|
+
) : (
|
|
111
|
+
<button onClick={() => void flow.next()} disabled={validating}>
|
|
112
|
+
Next
|
|
113
|
+
</button>
|
|
114
|
+
)}
|
|
88
115
|
</div>
|
|
89
116
|
);
|
|
90
117
|
}
|
|
91
118
|
```
|
|
119
|
+
|
|
120
|
+
### Без сборки — `validateModel` по схеме шага
|
|
121
|
+
|
|
122
|
+
Тот же результат без `createForm`: переход проверяется `validateModel(model, stepSchema)`, полный
|
|
123
|
+
submit — по общей схеме (композиция шагов через `apply(...)`).
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import {
|
|
127
|
+
validate,
|
|
128
|
+
apply,
|
|
129
|
+
defineValidation,
|
|
130
|
+
validateModel,
|
|
131
|
+
type ValidationDefinition,
|
|
132
|
+
} from '@reformer/core/validation';
|
|
133
|
+
import { required, min } from '@reformer/core/validators';
|
|
134
|
+
|
|
135
|
+
const loanRules = defineValidation<Form>(({ model }) => {
|
|
136
|
+
validate(model.$.loanType, [required()]);
|
|
137
|
+
validate(model.$.loanAmount, [required(), min(50000)]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const applicantRules = defineValidation<Form>(({ model }) => {
|
|
141
|
+
validate(model.$.personalData.firstName, [required()]);
|
|
142
|
+
validate(model.$.personalData.lastName, [required()]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const STEP_RULES: readonly ValidationDefinition<Form>[] = [loanRules, applicantRules];
|
|
146
|
+
const fullRules = defineValidation<Form>(() => apply(...STEP_RULES));
|
|
147
|
+
|
|
148
|
+
// true = нет блокирующих замечаний (severity:'warning' не блокирует); замечания уже в нодах
|
|
149
|
+
const stepOk = await validateModel(model, STEP_RULES[index], { touch: true });
|
|
150
|
+
const allOk = await validateModel(model, fullRules, { touch: true });
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Поток без сборки собирает `createFormFlow({ steps, validateStep })` — см. `38-form-flow.md`.
|
|
154
|
+
|
|
155
|
+
### Удалено
|
|
156
|
+
|
|
157
|
+
| Прежний контракт | Сейчас |
|
|
158
|
+
| -------------------------------------------------------- | --------------------------------------------------------- |
|
|
159
|
+
| `validation: { steps: { loan: rules } }` | `flow: { steps: [{ id: 'loan', rules }] }` |
|
|
160
|
+
| `bundle.validation.validateStep(n)` / `.validateAll()` / `.runStep()` / `.runAll()` | `bundle.validateStep(id)` / `bundle.validate()` |
|
|
161
|
+
| `<FormWizard config={…} ref={…}>`, `FormWizardConfig`, `FormWizardHandle` | `<FormWizard flow={bundle.flow}>`, навигация — `bundle.flow` |
|
|
162
|
+
| `defineSteps`, `useWizardStepValidation` | `flow` сборки; живая проверка — `validation.strategy` |
|
|
@@ -58,41 +58,44 @@ compute(model.$.b, () => model.a + 1); // расходится → Cycle detecte
|
|
|
58
58
|
compute(model.$.total, () => model.price * model.qty); // одно направление
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
### Cross-field валидация — через `cross
|
|
61
|
+
### Cross-field валидация — через `cross` области схемы
|
|
62
62
|
|
|
63
|
-
Cross-field живёт в схеме валидации (`@reformer/core/validation`), а не в
|
|
64
|
-
ПОЛЕ-НОСИТЕЛЬ ошибки оператором `cross(sig,
|
|
63
|
+
Cross-field живёт в схеме валидации (`@reformer/core/validation`), а не в схеме формы: правило
|
|
64
|
+
вешается на ПОЛЕ-НОСИТЕЛЬ ошибки оператором `cross(sig, check)` из аргумента схемы, а `check`
|
|
65
|
+
получает снимок модели области.
|
|
65
66
|
|
|
66
67
|
```typescript
|
|
67
|
-
import {
|
|
68
|
+
import { defineValidation, validate, validateModel } from '@reformer/core/validation';
|
|
68
69
|
import { required } from '@reformer/core/validators';
|
|
69
|
-
import {
|
|
70
|
+
import { type ValidationIssue } from '@reformer/core';
|
|
71
|
+
import { revalidateWhen } from '@reformer/core/model';
|
|
70
72
|
|
|
71
73
|
// ❌ старое (УДАЛЕНО): дерево { value, validators }, ModelValidator (value, scope, root), validateFormModel
|
|
72
74
|
const legacyRule: ModelValidator<number, unknown, Form> = (_value, _scope, root) =>
|
|
73
|
-
root.field1 > root.field2 ? { code: 'error'
|
|
75
|
+
root.field1 > root.field2 ? { code: 'error' } : null;
|
|
74
76
|
const legacySchema = { field1: { value: model.$.field1, component: Input, validators: [legacyRule] } };
|
|
75
77
|
validateFormModel(model, legacySchema);
|
|
76
78
|
|
|
77
|
-
// ✅ новое: cross-правило — обычная функция над
|
|
78
|
-
const field1LessThanField2 = (
|
|
79
|
-
|
|
79
|
+
// ✅ новое: cross-правило — обычная функция над снимком модели
|
|
80
|
+
const field1LessThanField2 = (form: Form): ValidationIssue | null =>
|
|
81
|
+
form.field1 > form.field2 ? { code: 'field1.notLess' } : null;
|
|
80
82
|
|
|
81
|
-
const schema =
|
|
83
|
+
const schema = defineValidation<Form>(({ model, cross }) => {
|
|
82
84
|
validate(model.$.field1, [required()]);
|
|
83
|
-
cross(model.$.field1, field1LessThanField2); // правило на поле-носителе
|
|
85
|
+
cross(model.$.field1, field1LessThanField2); // правило на поле-носителе замечания
|
|
84
86
|
});
|
|
85
87
|
|
|
86
|
-
// Прогон —
|
|
87
|
-
//
|
|
88
|
+
// Прогон — уровнем формы (createForm({ …, validation: schema })): bundle.validate() /
|
|
89
|
+
// bundle.submission.submit(handler); без сборки — раннер. Замечания сами доезжают до нод формы.
|
|
88
90
|
await validateModel(model, schema);
|
|
89
91
|
|
|
90
92
|
// Перезапуск правила при изменении соседнего поля — мост «поведение → валидация»:
|
|
91
93
|
revalidateWhen([model.$.field2], () => void validateModel(model, schema));
|
|
92
94
|
```
|
|
93
95
|
|
|
94
|
-
> **Актуальный vs удалённый API.** Операторы валидации `validate`/`validateAsync`/`validateWhen`/`
|
|
95
|
-
>
|
|
96
|
-
>
|
|
96
|
+
> **Актуальный vs удалённый API.** Операторы валидации `validate`/`validateAsync`/`validateWhen`/`apply`/`applyEach`
|
|
97
|
+
> существуют — в `@reformer/core/validation` (активны только внутри прогона схемы); `cross` — в аргументе
|
|
98
|
+
> схемы. УДАЛЕНЫ: `applyWhen`, `validateGroup`/`validateTree`/`validateForm`/`validateFormModel`,
|
|
97
99
|
> типы `FieldPath`/`ValidationSchemaFn`/`BehaviorSchemaFn` и `ctx.form.*`/`ctx.setFieldValue` (наследие
|
|
98
|
-
> path-based архитектуры)
|
|
100
|
+
> path-based архитектуры), а также текст в правиле (`message`), методы проверки на нодах
|
|
101
|
+
> (`form.validate()` / `form.submit()`) и `validation.steps` сборки. См. `17-nonexistent-api.md`.
|