@reformer/core 11.0.0-beta.4 → 11.0.0-beta.6
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/dist/form/create-form.d.ts +6 -4
- package/dist/form/factories/node-factory.d.ts +1 -0
- package/dist/form/nodes/field-node.d.ts +3 -0
- package/dist/form/validators/email.d.ts +5 -6
- package/dist/form/validators/file-type.d.ts +5 -6
- package/dist/form/validators/future-date.d.ts +5 -6
- package/dist/form/validators/index.d.ts +3 -2
- package/dist/form/validators/integer.d.ts +5 -6
- package/dist/form/validators/is-date.d.ts +5 -6
- package/dist/form/validators/is-number.d.ts +5 -6
- package/dist/form/validators/max-age.d.ts +5 -6
- package/dist/form/validators/max-date.d.ts +5 -6
- package/dist/form/validators/max-file-size.d.ts +5 -6
- package/dist/form/validators/max-files.d.ts +5 -6
- package/dist/form/validators/max-length.d.ts +6 -7
- package/dist/form/validators/max-total-file-size.d.ts +5 -6
- package/dist/form/validators/max.d.ts +6 -7
- package/dist/form/validators/min-age.d.ts +5 -6
- package/dist/form/validators/min-date.d.ts +5 -6
- package/dist/form/validators/min-file-size.d.ts +5 -6
- package/dist/form/validators/min-files.d.ts +5 -6
- package/dist/form/validators/min-length.d.ts +6 -7
- package/dist/form/validators/min.d.ts +6 -7
- package/dist/form/validators/multiple-of.d.ts +5 -6
- package/dist/form/validators/non-negative.d.ts +5 -6
- package/dist/form/validators/non-zero.d.ts +5 -6
- package/dist/form/validators/past-date.d.ts +5 -6
- package/dist/form/validators/pattern.d.ts +6 -14
- package/dist/form/validators/phone.d.ts +5 -6
- package/dist/form/validators/required.d.ts +7 -12
- package/dist/form/validators/url.d.ts +7 -12
- package/dist/index.js +423 -376
- package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
- package/dist/platforms/react/field-binding/field-handle.d.ts +50 -0
- package/dist/platforms/react/index.d.ts +4 -0
- package/docs/llms/02-quick-start.md +3 -3
- package/docs/llms/03-api-signatures.md +4 -4
- package/docs/llms/09-formschema.md +5 -5
- package/docs/llms/10-arrays.md +2 -2
- package/docs/llms/15-project-structure.md +19 -16
- package/docs/llms/16-ui-components.md +34 -7
- package/docs/llms/17-nonexistent-api.md +14 -0
- package/docs/llms/29-async-preload.md +3 -3
- package/docs/llms/30-type-safety-recipes.md +1 -1
- package/llms-index.json +1 -0
- package/llms.txt +2146 -68
- package/package.json +2 -1
package/llms.txt
CHANGED
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
|
|
43
43
|
## 1. 1. API Reference
|
|
44
44
|
|
|
45
|
+
**api-reference**
|
|
46
|
+
|
|
45
47
|
### Imports (CRITICALLY IMPORTANT)
|
|
46
48
|
|
|
47
49
|
Архитектура M1: значения живут в **модели** (`createModel`), а форма (`createForm`) строит ноды поверх сигналов модели. Behaviors работают на сигналах (`model.$.field`), а не на строковых путях.
|
|
@@ -106,6 +108,8 @@ const { value, errors, disabled } = useFormControl(control.loanType);
|
|
|
106
108
|
|
|
107
109
|
## 2. 1.5 QUICK START - Minimal Working Form
|
|
108
110
|
|
|
111
|
+
**quick-start**
|
|
112
|
+
|
|
109
113
|
> **Schema-driven UI rule (read first)**: компонент И его пропсы (label, placeholder,
|
|
110
114
|
> options, type) объявляются в **схеме поля** (`component` + `componentProps`).
|
|
111
115
|
> В JSX рендерится один универсальный `<FormField control={form.x} />` из
|
|
@@ -219,14 +223,14 @@ const model = createModel<MyForm>({ properties: [] });
|
|
|
219
223
|
const propertyItem = (item: FormModel<PropertyItem>) => ({
|
|
220
224
|
type: {
|
|
221
225
|
value: item.$.type,
|
|
222
|
-
component:
|
|
226
|
+
component: SelectAsync,
|
|
223
227
|
componentProps: { label: 'Тип', options: [/* ... */] },
|
|
224
228
|
},
|
|
225
229
|
description: { value: item.$.description, component: Textarea, componentProps: { label: 'Описание' } },
|
|
226
230
|
estimatedValue: {
|
|
227
231
|
value: item.$.estimatedValue,
|
|
228
|
-
component:
|
|
229
|
-
componentProps: { label: 'Стоимость'
|
|
232
|
+
component: InputNumber,
|
|
233
|
+
componentProps: { label: 'Стоимость' },
|
|
230
234
|
},
|
|
231
235
|
});
|
|
232
236
|
|
|
@@ -257,6 +261,8 @@ model.properties.length; // реактивная длина
|
|
|
257
261
|
|
|
258
262
|
## 3. 2. API SIGNATURES
|
|
259
263
|
|
|
264
|
+
**api-signatures**
|
|
265
|
+
|
|
260
266
|
### Model & Form
|
|
261
267
|
|
|
262
268
|
```typescript
|
|
@@ -322,16 +328,16 @@ import { createModel, createForm } from '@reformer/core';
|
|
|
322
328
|
import { defineValidationSchema, validate, validateModel } from '@reformer/core/validation';
|
|
323
329
|
import { required, min, max, email } from '@reformer/core/validators';
|
|
324
330
|
|
|
325
|
-
type Loan = { email: string; age: number; amount: number };
|
|
326
|
-
const model = createModel<Loan>({ email: '', age:
|
|
331
|
+
type Loan = { email: string; age: number | null; amount: number | null };
|
|
332
|
+
const model = createModel<Loan>({ email: '', age: null, amount: null });
|
|
327
333
|
|
|
328
334
|
// layout-схема createForm НЕ несёт validators — только привязка поля к сигналу + компонент
|
|
329
335
|
const form = createForm({
|
|
330
336
|
model,
|
|
331
337
|
schema: {
|
|
332
338
|
email: { value: model.$.email, component: Input },
|
|
333
|
-
age: { value: model.$.age, component:
|
|
334
|
-
amount: { value: model.$.amount, component:
|
|
339
|
+
age: { value: model.$.age, component: InputNumber },
|
|
340
|
+
amount: { value: model.$.amount, component: InputNumber },
|
|
335
341
|
},
|
|
336
342
|
});
|
|
337
343
|
|
|
@@ -531,6 +537,8 @@ revalidateWhen([model.$.dep], () => void validateModel(model, schema));
|
|
|
531
537
|
|
|
532
538
|
## 4. 3. COMMON PATTERNS
|
|
533
539
|
|
|
540
|
+
**common-patterns**
|
|
541
|
+
|
|
534
542
|
Все паттерны — на архитектуре M1: значения в модели (`model.$.field`), поведение (`defineFormBehavior`)
|
|
535
543
|
на живых сигналах, валидация — отдельной ambient-схемой (`defineValidationSchema`, прогон по требованию
|
|
536
544
|
через `validateModel`). Слои раздельны: layout НЕ несёт validators.
|
|
@@ -734,6 +742,8 @@ const schema = defineValidationSchema<MyForm>(({ model }) => {
|
|
|
734
742
|
|
|
735
743
|
## 5. 4. COMMON MISTAKES
|
|
736
744
|
|
|
745
|
+
**common-mistakes**
|
|
746
|
+
|
|
737
747
|
### Imports rule (#1 cause of cascading errors — read first)
|
|
738
748
|
|
|
739
749
|
- Модель/форма/хуки/типы (`FormModel`, `ValidationError`)/**примитивы behaviors** — из `@reformer/core`.
|
|
@@ -902,6 +912,8 @@ if (isFieldNode(node)) { ... }
|
|
|
902
912
|
|
|
903
913
|
## 6. 5. TROUBLESHOOTING
|
|
904
914
|
|
|
915
|
+
**troubleshooting**
|
|
916
|
+
|
|
905
917
|
| Error | Cause | Solution |
|
|
906
918
|
| ------------------------------------------------------ | -------------------------------------------------------- | ------------------------------------------------- |
|
|
907
919
|
| `'string' is not assignable to '{ message?: string }'` | Валидатору передали строку вместо options | Используй `required({ message: 'text' })` |
|
|
@@ -914,6 +926,8 @@ if (isFieldNode(node)) { ... }
|
|
|
914
926
|
|
|
915
927
|
## 7. Import Patterns
|
|
916
928
|
|
|
929
|
+
**complete-import**
|
|
930
|
+
|
|
917
931
|
```typescript
|
|
918
932
|
// Модель, форма, хуки, типы, примитивы behaviors — из @reformer/core
|
|
919
933
|
import {
|
|
@@ -1014,6 +1028,8 @@ export type CoBorrower = {
|
|
|
1014
1028
|
|
|
1015
1029
|
## 8. 7. FORM TYPE DEFINITION
|
|
1016
1030
|
|
|
1031
|
+
**form-types**
|
|
1032
|
+
|
|
1017
1033
|
Form-shape объявляй через `type`-alias (не `interface` — см. `30-type-safety-recipes.md`).
|
|
1018
1034
|
Тип описывает форму ДАННЫХ (то, что кладётся в `createModel`), без узлов/сигналов.
|
|
1019
1035
|
|
|
@@ -1062,6 +1078,8 @@ const model = createModel<MyForm>({
|
|
|
1062
1078
|
|
|
1063
1079
|
## 9. 8. SCHEMA FORMAT (CRITICALLY IMPORTANT)
|
|
1064
1080
|
|
|
1081
|
+
**formschema**
|
|
1082
|
+
|
|
1065
1083
|
Под M1 схема **привязывает поле к сигналу модели** (`value: model.$.field`) и держит
|
|
1066
1084
|
UI-конфиг (`component`/`componentProps`). Значения принадлежат модели. Валидаторов в
|
|
1067
1085
|
layout-схеме НЕТ — правила живут в отдельной схеме `defineValidationSchema` из
|
|
@@ -1090,7 +1108,7 @@ layout-схеме НЕТ — правила живут в отдельной с
|
|
|
1090
1108
|
import { createModel, createForm } from '@reformer/core';
|
|
1091
1109
|
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
1092
1110
|
import { required } from '@reformer/core/validators';
|
|
1093
|
-
import { Input,
|
|
1111
|
+
import { Input, InputNumber, SelectAsync, CheckboxWithLabel } from '@reformer/ui-kit';
|
|
1094
1112
|
|
|
1095
1113
|
const model = createModel<MyForm>({ name: '', age: null, agree: false, status: 'active' });
|
|
1096
1114
|
|
|
@@ -1107,17 +1125,17 @@ const schema = {
|
|
|
1107
1125
|
},
|
|
1108
1126
|
age: {
|
|
1109
1127
|
value: model.$.age,
|
|
1110
|
-
component:
|
|
1111
|
-
componentProps: {
|
|
1128
|
+
component: InputNumber,
|
|
1129
|
+
componentProps: { label: 'Age' },
|
|
1112
1130
|
},
|
|
1113
1131
|
agree: {
|
|
1114
1132
|
value: model.$.agree,
|
|
1115
|
-
component:
|
|
1133
|
+
component: CheckboxWithLabel,
|
|
1116
1134
|
componentProps: { label: 'I agree to terms' },
|
|
1117
1135
|
},
|
|
1118
1136
|
status: {
|
|
1119
1137
|
value: model.$.status,
|
|
1120
|
-
component:
|
|
1138
|
+
component: SelectAsync,
|
|
1121
1139
|
componentProps: {
|
|
1122
1140
|
label: 'Status',
|
|
1123
1141
|
options: [
|
|
@@ -1206,6 +1224,8 @@ if (isFieldNode(node)) { /* ... */ }
|
|
|
1206
1224
|
|
|
1207
1225
|
## 10. 9. ARRAY SCHEMA FORMAT
|
|
1208
1226
|
|
|
1227
|
+
**arrays**
|
|
1228
|
+
|
|
1209
1229
|
Массивы объектов — **model-owned**: данные принадлежат модели (`model.arrayField` — это
|
|
1210
1230
|
`ModelArray<Item>` с реактивными `push`/`removeAt`/`length`). В схеме массив объявляется узлом
|
|
1211
1231
|
`{ array: model.<path>, item: (itemModel) => subSchema }`, где `item` строит под-схему одного
|
|
@@ -1213,7 +1233,7 @@ if (isFieldNode(node)) { /* ... */ }
|
|
|
1213
1233
|
|
|
1214
1234
|
```typescript
|
|
1215
1235
|
import { createModel, createForm, type FormModel } from '@reformer/core';
|
|
1216
|
-
import { Input } from '@reformer/ui-kit';
|
|
1236
|
+
import { Input, InputNumber } from '@reformer/ui-kit';
|
|
1217
1237
|
|
|
1218
1238
|
type Item = { id: string; name: string; price: number };
|
|
1219
1239
|
type MyForm = { items: Item[] };
|
|
@@ -1224,7 +1244,7 @@ const model = createModel<MyForm>({ items: [] });
|
|
|
1224
1244
|
const itemSchema = (item: FormModel<Item>) => ({
|
|
1225
1245
|
id: { value: item.$.id, component: Input },
|
|
1226
1246
|
name: { value: item.$.name, component: Input },
|
|
1227
|
-
price: { value: item.$.price, component:
|
|
1247
|
+
price: { value: item.$.price, component: InputNumber },
|
|
1228
1248
|
});
|
|
1229
1249
|
|
|
1230
1250
|
const schema = {
|
|
@@ -1368,6 +1388,8 @@ cross(model.$.hasItems, (f: MyForm) => {
|
|
|
1368
1388
|
|
|
1369
1389
|
## 12. 10. ASYNC REACTION — onChange (CRITICALLY IMPORTANT)
|
|
1370
1390
|
|
|
1391
|
+
**async-watchfield**
|
|
1392
|
+
|
|
1371
1393
|
Для async-реакции на изменение поля (загрузка зависимых опций, справочников) используй
|
|
1372
1394
|
`onChange` из `@reformer/core/behaviors`. Колбэк выполняется ВНЕ effect-контекста (можно
|
|
1373
1395
|
безопасно писать сигналы/ноды), а 2-й аргумент — `{ signal }` (AbortSignal): при следующей
|
|
@@ -1422,6 +1444,8 @@ const stop = watchField(model.$.country, (country) => {
|
|
|
1422
1444
|
|
|
1423
1445
|
## 13. 11. ARRAY CLEANUP PATTERN
|
|
1424
1446
|
|
|
1447
|
+
**array-cleanup**
|
|
1448
|
+
|
|
1425
1449
|
Очистка массива при выключении флага — через `onChange` на сигнале флага + `clear()` на
|
|
1426
1450
|
модели-массиве. Колбит выполняется вне effect-контекста, поэтому мутировать массив безопасно.
|
|
1427
1451
|
|
|
@@ -1454,6 +1478,8 @@ clearWhenOff(model.$.hasProperty, model.properties);
|
|
|
1454
1478
|
|
|
1455
1479
|
## 14. 12. MULTI-STEP FORM VALIDATION
|
|
1456
1480
|
|
|
1481
|
+
**multi-step**
|
|
1482
|
+
|
|
1457
1483
|
Каждый шаг — своя `ValidationSchema<Form>` (обычная функция `({ model }) => void`, обёрнутая
|
|
1458
1484
|
`defineValidationSchema`). Переход к следующему шагу проверяется `validateModel(model, stepSchema)`;
|
|
1459
1485
|
полный submit — по общей схеме (композиция шагов через `apply(...)`). `validateModel` сам разносит
|
|
@@ -1546,6 +1572,8 @@ function MultiStepForm() {
|
|
|
1546
1572
|
|
|
1547
1573
|
## 15. 13. EXTENDED COMMON MISTAKES
|
|
1548
1574
|
|
|
1575
|
+
**extended-mistakes**
|
|
1576
|
+
|
|
1549
1577
|
### Reuse via apply / applyEach — не дублируй код
|
|
1550
1578
|
|
|
1551
1579
|
```typescript
|
|
@@ -1645,13 +1673,17 @@ revalidateWhen([model.$.field2], () => void validateModel(model, schema));
|
|
|
1645
1673
|
|
|
1646
1674
|
## 16. 14. PROJECT STRUCTURE (COLOCATION)
|
|
1647
1675
|
|
|
1676
|
+
**project-structure**
|
|
1677
|
+
|
|
1648
1678
|
One form is one module folder. **Default layout — flat minimalist:** every concern is a single
|
|
1649
1679
|
file at the module root, and the whole form (entry + all wizard steps inline) lives in one
|
|
1650
|
-
`index.tsx`.
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1680
|
+
`index.tsx`. Naming rule: **`form.<role>` is a form artefact, the suffix names its role** —
|
|
1681
|
+
`form.schema.*` (layout), `form.behavior.ts` (model behavior), `form.render.ts` (render-layer
|
|
1682
|
+
behavior, renderer targets only), `form.validation.ts` (validation rules); every other file has
|
|
1683
|
+
no prefix. A wizard may instead keep each
|
|
1684
|
+
step's code in `steps/<slug>/` (same file names inside) with a `steps/index.ts` aggregator. The old
|
|
1685
|
+
`renderer.schema.*` / `renderer.behavior.ts` / `renderer.wizard.tsx` / `validation.ts` names still
|
|
1686
|
+
work with a warning. No `lib/` / `schema/` / `components/` nesting until the form grows (see "Scaling up" below). Arrays are declared in `form.schema.ts`
|
|
1655
1687
|
and rendered with `FormArraySection` — no per-step component files.
|
|
1656
1688
|
|
|
1657
1689
|
```
|
|
@@ -1665,13 +1697,13 @@ src/
|
|
|
1665
1697
|
│ ├── model.ts # createModel + initial values + empty-array-element factories
|
|
1666
1698
|
│ ├── form.schema.ts # FormSchema: { value: model.$.x, component, componentProps }
|
|
1667
1699
|
│ ├── form.behavior.ts # defineFormBehavior: compute / enableWhen / hideWhen / copyFrom / onChange
|
|
1668
|
-
│ ├── validation.ts
|
|
1700
|
+
│ ├── form.validation.ts # ALL validation → { validateStep, validateAll }
|
|
1669
1701
|
│ ├── data-sources.ts # options + async loaders (dataSources)
|
|
1670
1702
|
│ └── api.ts # submit + prefill/load
|
|
1671
1703
|
```
|
|
1672
1704
|
|
|
1673
1705
|
Rule of thumb: **one concern → one file at the module root; the whole component tree (all steps) →
|
|
1674
|
-
`index.tsx`; validation is a single `validation.ts`.** This is the working default for
|
|
1706
|
+
`index.tsx`; validation is a single `form.validation.ts`.** This is the working default for
|
|
1675
1707
|
almost every form — reach for folders only when a file stops fitting on a screen.
|
|
1676
1708
|
|
|
1677
1709
|
### Key Files
|
|
@@ -1692,7 +1724,7 @@ export const createCreditApplicationModel = (): FormModel<CreditApplicationForm>
|
|
|
1692
1724
|
// forms/credit-application/form.schema.ts
|
|
1693
1725
|
import type { FormModel } from '@reformer/core';
|
|
1694
1726
|
export const creditApplicationSchema = (model: FormModel<CreditApplicationForm>) => ({
|
|
1695
|
-
loanType: { value: model.$.loanType, component:
|
|
1727
|
+
loanType: { value: model.$.loanType, component: SelectAsync, componentProps: { /* ... */ } },
|
|
1696
1728
|
personalData: personalDataNodes(model.$.personalData),
|
|
1697
1729
|
properties: { array: model.properties, item: propertyItem },
|
|
1698
1730
|
});
|
|
@@ -1739,9 +1771,9 @@ forms/
|
|
|
1739
1771
|
│
|
|
1740
1772
|
├── schema/ # The form definition
|
|
1741
1773
|
│ ├── model.ts # createModel factory + initial values + array-element factories
|
|
1742
|
-
│ ├── schema.ts
|
|
1743
|
-
│ ├── validation.ts
|
|
1744
|
-
│ ├── behavior.ts
|
|
1774
|
+
│ ├── form.schema.ts # schema builder (model) => tree ({ value: model.$.x, component })
|
|
1775
|
+
│ ├── form.validation.ts # defineValidationSchema + validateModel config ({ validateStep, validateAll })
|
|
1776
|
+
│ ├── form.behavior.ts # defineFormBehavior(...)
|
|
1745
1777
|
│ └── create-form.ts # Assembly: createCoreForm({ model, schema, behavior, validation })
|
|
1746
1778
|
│
|
|
1747
1779
|
└── components/
|
|
@@ -1752,7 +1784,7 @@ forms/
|
|
|
1752
1784
|
|
|
1753
1785
|
Rule of thumb for this layout: **domain raw material → `lib/`; anything describing the form →
|
|
1754
1786
|
`schema/`; React layout → `components/`; root = entry + `index.ts`.** In very large forms you
|
|
1755
|
-
may co-locate each step's `schema.ts` / `validation.ts` / `behavior.ts` inside its
|
|
1787
|
+
may co-locate each step's `form.schema.ts` / `form.validation.ts` / `form.behavior.ts` inside its
|
|
1756
1788
|
`steps/[Step]/` folder, keeping only the shared `model` and cross-step rules in `schema/` — see
|
|
1757
1789
|
the guide below.
|
|
1758
1790
|
|
|
@@ -1768,21 +1800,25 @@ the guide below.
|
|
|
1768
1800
|
> when registering the MCP server to choose which structure the generators lead with. Default
|
|
1769
1801
|
> is `minimalist`.
|
|
1770
1802
|
|
|
1771
|
-
> Cross-target variants (renderer-react `
|
|
1772
|
-
> renderer-json `
|
|
1773
|
-
>
|
|
1774
|
-
> **form-directory-layout** guide (`@reformer/mcp`)
|
|
1803
|
+
> Cross-target variants (renderer-react `form.schema.ts` + `form.render.ts` /
|
|
1804
|
+
> renderer-json `form.schema.ts` + `form.render.ts` + `registry.ts`, plus an optional
|
|
1805
|
+
> `wizard.tsx` shim), the `.tsx` / `.json` schema variants, the centralized-vs-co-located
|
|
1806
|
+
> choice, and the full reuse map live in the **form-directory-layout** guide (`@reformer/mcp`) —
|
|
1807
|
+
> `find_recipe directory-layout`.
|
|
1775
1808
|
|
|
1776
1809
|
## 17. 14.5 UI COMPONENT PATTERNS
|
|
1777
1810
|
|
|
1811
|
+
**ui-components**
|
|
1812
|
+
|
|
1778
1813
|
> **Default rule (read first)**: для UI используй `FormField` из
|
|
1779
1814
|
> [`@reformer/ui-kit`](../../reformer-ui-kit/) — он покрывает 95% случаев одной
|
|
1780
|
-
> строкой `<FormField control={form.x} />`. Свои
|
|
1815
|
+
> строкой `<FormField control={form.x} />`. Свои обёртки поля пиши ТОЛЬКО если
|
|
1781
1816
|
> ui-kit не подходит (другая design system, особый low-level input).
|
|
1782
1817
|
>
|
|
1783
1818
|
> Канонический schema-driven подход:
|
|
1784
1819
|
>
|
|
1785
|
-
> - **компонент** объявляется в схеме как `component: Input` (или `
|
|
1820
|
+
> - **компонент** объявляется в схеме как `component: Input` (или `SelectAsync`, `CheckboxWithLabel`, …) —
|
|
1821
|
+
> сам компонент кита, отдельных «field-версий» нет: связывает его с полем обёртка (`FormField`)
|
|
1786
1822
|
> - **пропсы** компонента — в `componentProps: { label, placeholder, options, type, ... }`
|
|
1787
1823
|
> - **JSX рендерит**: `<FormField control={form.x} />` БЕЗ дополнительных props
|
|
1788
1824
|
>
|
|
@@ -1794,7 +1830,7 @@ the guide below.
|
|
|
1794
1830
|
```tsx
|
|
1795
1831
|
import { useMemo } from 'react';
|
|
1796
1832
|
import { createModel, createForm } from '@reformer/core';
|
|
1797
|
-
import { FormField, Input,
|
|
1833
|
+
import { FormField, Input, SelectAsync, CheckboxWithLabel, Button } from '@reformer/ui-kit';
|
|
1798
1834
|
|
|
1799
1835
|
type RegistrationForm = {
|
|
1800
1836
|
email: string;
|
|
@@ -1813,7 +1849,7 @@ function RegistrationPage() {
|
|
|
1813
1849
|
},
|
|
1814
1850
|
country: {
|
|
1815
1851
|
value: model.$.country,
|
|
1816
|
-
component:
|
|
1852
|
+
component: SelectAsync,
|
|
1817
1853
|
componentProps: {
|
|
1818
1854
|
label: 'Country',
|
|
1819
1855
|
options: [
|
|
@@ -1824,7 +1860,7 @@ function RegistrationPage() {
|
|
|
1824
1860
|
},
|
|
1825
1861
|
agree: {
|
|
1826
1862
|
value: model.$.agree,
|
|
1827
|
-
component:
|
|
1863
|
+
component: CheckboxWithLabel,
|
|
1828
1864
|
componentProps: { label: 'I agree to terms' },
|
|
1829
1865
|
},
|
|
1830
1866
|
};
|
|
@@ -1888,7 +1924,8 @@ import { InputMask } from 'react-input-mask';
|
|
|
1888
1924
|
```
|
|
1889
1925
|
|
|
1890
1926
|
`children` оборачивается в `CdkFormField.Control asChild` и получает все нужные
|
|
1891
|
-
props (`value`, `onChange`, `onBlur`, `aria-invalid`)
|
|
1927
|
+
props (`value`, `onChange`, `onBlur`, `aria-invalid`) — в диалекте ребёнка, если тот объявил
|
|
1928
|
+
статику `reformerAdapter` (`defineFieldControl` из `@reformer/ui-kit/fields`), иначе value-based.
|
|
1892
1929
|
|
|
1893
1930
|
### Advanced — write your own from scratch (rare)
|
|
1894
1931
|
|
|
@@ -1930,12 +1967,37 @@ function MyFormField<T>({ control }: MyFormFieldProps<T>) {
|
|
|
1930
1967
|
<MyFormField control={form.email} /> // ← без label-prop
|
|
1931
1968
|
```
|
|
1932
1969
|
|
|
1970
|
+
### Связывание поля с контролом — `FieldAdapter` (`@reformer/core`)
|
|
1971
|
+
|
|
1972
|
+
Контролы говорят на разных диалектах (`checked` + `onCheckedChange`, `onValueChange`, DOM-событие
|
|
1973
|
+
в `onChange`), форма — на value-based seam (`value` + `onChange(value)` + `onBlur`). Перевод
|
|
1974
|
+
описывает `FieldAdapter` (`valueProp`, `changeProp`, `fromEmit`, `toValue`, `bindBlur`, `strip`,
|
|
1975
|
+
`passControl` — все необязательны). Компонент объявляет его статикой `reformerAdapter`, а обёртка
|
|
1976
|
+
поля (`FormField.Control` из `@reformer/cdk`, рендерер `@reformer/renderer-react`) читает статику
|
|
1977
|
+
и связывает поле сама — отдельные «field-версии» компонентов не нужны.
|
|
1978
|
+
|
|
1979
|
+
| Символ | Назначение |
|
|
1980
|
+
| ----------------------------- | --------------------------------------------------------------------------- |
|
|
1981
|
+
| `FieldAdapter` | Тип адаптера: диалект контрола |
|
|
1982
|
+
| `getFieldAdapter(component)` | Статика `component.reformerAdapter` либо `undefined` |
|
|
1983
|
+
| `bindFieldProps(adapter, seam, props)` | Props контрола под адаптер (без адаптера — seam как есть) |
|
|
1984
|
+
| `FieldHandle` | Императивный handle поля: `focus` / `blur` / `scrollIntoView` / `getElement` |
|
|
1985
|
+
| `makeElementFieldHandle(ref)` | Базовый `FieldHandle` из DOM-узла |
|
|
1986
|
+
| `useFieldHandleRef(outerRef)` | Ref для контрола + публикация handle потребителю (строит ОБЁРТКА поля) |
|
|
1987
|
+
|
|
1988
|
+
В приложении обычно достаточно `defineFieldControl(MyControl, { adapter })` из
|
|
1989
|
+
`@reformer/ui-kit/fields` (плюс готовые пресеты `checkedAdapter`, `valueChangeAdapter`, …); для
|
|
1990
|
+
компонентов чужой библиотеки без статики — `RendererSettings.resolveFieldAdapter`.
|
|
1991
|
+
|
|
1933
1992
|
### Integration with UI libraries (shadcn etc.)
|
|
1934
1993
|
|
|
1935
1994
|
> **Через рендерер — без обёртки.** Если ты рендеришь форму через `@reformer/renderer-react` /
|
|
1936
1995
|
> `@reformer/renderer-json`, вместо ручной обёртки на каждый контрол можно зарегистрировать сырой
|
|
1937
1996
|
> компонент и передать `settings.resolveFieldAdapter` — рендерер сам сведёт value-seam к диалекту
|
|
1938
|
-
> контрола.
|
|
1997
|
+
> контрола. Свой компонент можно подготовить и один раз — статикой
|
|
1998
|
+
> `defineFieldControl(MyInput, { adapter })` из `@reformer/ui-kit/fields`: его поймут и рендерер,
|
|
1999
|
+
> и `FormField.Control` из `@reformer/cdk`. Ручная обёртка на `useFormControl` (ниже) нужна для
|
|
2000
|
+
> прямого JSX без `FormField` и рендерера.
|
|
1939
2001
|
|
|
1940
2002
|
Если есть существующая design system — оборачивай её компоненты в один
|
|
1941
2003
|
`MyFormField` (как выше) и используй один прop `control`. Не множь обёртки на
|
|
@@ -1965,6 +2027,8 @@ function ShadcnFormField({ control }: { control: FieldNode<string> }) {
|
|
|
1965
2027
|
|
|
1966
2028
|
## 18. 15. NON-EXISTENT API (DO NOT USE)
|
|
1967
2029
|
|
|
2030
|
+
**nonexistent-api**
|
|
2031
|
+
|
|
1968
2032
|
**Следующего API НЕТ в @reformer/core** (наследие старой path-based архитектуры,
|
|
1969
2033
|
удалено при переходе на M1 и при разделении валидации/поведения):
|
|
1970
2034
|
|
|
@@ -1991,6 +2055,20 @@ function ShadcnFormField({ control }: { control: FieldNode<string> }) {
|
|
|
1991
2055
|
| `FormProvider`, `control` prop, `register()` | `<Component form={form} />`, `useFormControl(form.field)` | Форма передаётся через props |
|
|
1992
2056
|
| `getFieldValue()` | `model.field` / `useFormControlValue(form.field)`| Не существует |
|
|
1993
2057
|
|
|
2058
|
+
### UI: удалённые field-версии компонентов (`@reformer/ui-kit`)
|
|
2059
|
+
|
|
2060
|
+
Отдельных «field-версий» у компонентов кита больше нет: в `component` поля кладётся сам
|
|
2061
|
+
компонент, диалект он объявляет статикой `reformerAdapter`, связывает поле обёртка
|
|
2062
|
+
(`FormField.Control` / рендерер).
|
|
2063
|
+
|
|
2064
|
+
| Wrong | Correct |
|
|
2065
|
+
| ---------------------------------------------- | --------------------------------------------------------- |
|
|
2066
|
+
| `InputField`, `SelectField`, `CheckboxField`, … (любой `*Field`) | `Input`, `SelectAsync`, `CheckboxWithLabel`, … — таблица в ui-kit `01-overview.md` |
|
|
2067
|
+
| `InputField` + `type: 'number'` | `InputNumber` |
|
|
2068
|
+
| `InputField` + `suggestions` | `InputSuggest` |
|
|
2069
|
+
| `FileUploadField` + `variant: 'dropzone' \| 'input'` | `FileUploadDropzone` / `FileUploadInput` (без `variant`) |
|
|
2070
|
+
| `withFormControl(MyControl, adapter)` | `defineFieldControl(MyControl, { adapter })` из `@reformer/ui-kit/fields` |
|
|
2071
|
+
|
|
1994
2072
|
### Common Import Errors
|
|
1995
2073
|
|
|
1996
2074
|
```typescript
|
|
@@ -2059,6 +2137,8 @@ revalidateWhen([model.$.password], () => void validateModel(model, validation));
|
|
|
2059
2137
|
|
|
2060
2138
|
## 19. Purpose
|
|
2061
2139
|
|
|
2140
|
+
**Условные поля — видимость, доступность и валидация**
|
|
2141
|
+
|
|
2062
2142
|
«Показать поле X только если Y» под M1 решается тремя независимыми механизмами — выбирай по
|
|
2063
2143
|
тому, что именно должно быть условным:
|
|
2064
2144
|
|
|
@@ -2241,6 +2321,8 @@ function MortgageFields({ form }: { form: CreditForm }) {
|
|
|
2241
2321
|
|
|
2242
2322
|
## 25. 16. READING FIELD VALUES (CRITICALLY IMPORTANT)
|
|
2243
2323
|
|
|
2324
|
+
**reading-values**
|
|
2325
|
+
|
|
2244
2326
|
Под M1 значения живут в модели. Есть три контекста чтения: value-доступ модели, сигналы, и React-хуки.
|
|
2245
2327
|
|
|
2246
2328
|
### В React-компоненте — хуки
|
|
@@ -2330,6 +2412,8 @@ const behavior = defineFormBehavior<MyForm>(({ model, form }) => {
|
|
|
2330
2412
|
|
|
2331
2413
|
## 26. 17. COMPUTE vs ONCHANGE
|
|
2332
2414
|
|
|
2415
|
+
**compute-vs-watch**
|
|
2416
|
+
|
|
2333
2417
|
Под M1 behaviors работают на сигналах модели. Есть два способа их писать:
|
|
2334
2418
|
|
|
2335
2419
|
- **примитивы из `@reformer/core`** — принимают сигналы, возвращают cleanup, вызываются
|
|
@@ -2478,6 +2562,8 @@ watchField(model.$, (all) => autosave(all)); // любое поле ф
|
|
|
2478
2562
|
|
|
2479
2563
|
## 27. 18. ARRAY OPERATIONS
|
|
2480
2564
|
|
|
2565
|
+
**array-operations**
|
|
2566
|
+
|
|
2481
2567
|
Массивы объектов — model-owned. Мутации делаются через `ModelArray` (`model.arrayField`);
|
|
2482
2568
|
рендер — через ноду формы (`form.items.map` / `.at`).
|
|
2483
2569
|
|
|
@@ -2592,6 +2678,8 @@ cross(model.$.totalPercent, percentagesSumTo100); // носитель — ска
|
|
|
2592
2678
|
|
|
2593
2679
|
## 28. Как устроено под M1
|
|
2594
2680
|
|
|
2681
|
+
**Cycle Detection — предотвращение «Cycle detected»**
|
|
2682
|
+
|
|
2595
2683
|
Behaviors на сигналах уже защищены от типичных циклов:
|
|
2596
2684
|
|
|
2597
2685
|
- `compute`/`computeFrom` пишут в цель только если значение изменилось (**peek-guard**), а цель
|
|
@@ -2677,6 +2765,8 @@ enableWhen([model.$.propertyValue, model.$.initialPayment], () => model.loanType
|
|
|
2677
2765
|
|
|
2678
2766
|
## 32. Purpose
|
|
2679
2767
|
|
|
2768
|
+
**copyFrom — Копирование значений между полями**
|
|
2769
|
+
|
|
2680
2770
|
`copyFrom` декларативно копирует значение одного поля (или группы) в другое при выполнении
|
|
2681
2771
|
условия `when`. Используется для UX-сценариев «совпадает с …»: «адрес проживания = адрес
|
|
2682
2772
|
регистрации», «email для уведомлений = основной email», «billing = shipping». Оператор сам
|
|
@@ -2817,6 +2907,8 @@ A: `copyFrom` при `when === false` просто не пишет (не сбр
|
|
|
2817
2907
|
|
|
2818
2908
|
## 38. Purpose
|
|
2819
2909
|
|
|
2910
|
+
**syncFields — Двусторонняя синхронизация полей**
|
|
2911
|
+
|
|
2820
2912
|
`syncFields` создаёт двунаправленную связь между двумя полями: изменение любого из них
|
|
2821
2913
|
переписывает второе. Применяется для дублей одного значения в разных частях формы
|
|
2822
2914
|
(тех. поле + видимое представление, mirror-поля). Внутренний флаг + `runOutsideEffect`
|
|
@@ -2930,6 +3022,8 @@ A: У `syncFields` нет `when`. Эмулируй через два `copyFrom(a
|
|
|
2930
3022
|
|
|
2931
3023
|
## 44. Purpose
|
|
2932
3024
|
|
|
3025
|
+
**resetWhen — Условный сброс полей**
|
|
3026
|
+
|
|
2933
3027
|
`resetWhen` сбрасывает значение поля к `resetValue`, когда `condition` истинно. Это
|
|
2934
3028
|
альтернатива `enableWhen({ resetOnDisable: true })`, когда поле остаётся **enabled**, но
|
|
2935
3029
|
содержимое нужно очистить (например, переключение способа оплаты обнуляет «номер карты», но
|
|
@@ -3044,6 +3138,8 @@ A: `resetWhen` рассчитан на скалярные сигналы. Для
|
|
|
3044
3138
|
|
|
3045
3139
|
## 50. Purpose
|
|
3046
3140
|
|
|
3141
|
+
**transformValue — Автоматическая трансформация значений**
|
|
3142
|
+
|
|
3047
3143
|
`transformValue` подписывается на изменение поля и переписывает его трансформированной
|
|
3048
3144
|
версией: uppercase для кодов, trim+toLowerCase для email, округление для чисел. Применяется
|
|
3049
3145
|
единообразно ко всем источникам изменения (пользователь, `model.field = …`, `model.set/patch`,
|
|
@@ -3179,6 +3275,8 @@ A: Симптом частых записей. Форматирование с
|
|
|
3179
3275
|
|
|
3180
3276
|
## 56. Purpose
|
|
3181
3277
|
|
|
3278
|
+
**revalidateWhen — Перевалидация по триггерам**
|
|
3279
|
+
|
|
3182
3280
|
`revalidateWhen` вызывает переданный колбэк ревалидации, когда изменяется любая из
|
|
3183
3281
|
зависимостей. Применяется, когда правило зависит от значений других полей
|
|
3184
3282
|
(`amount <= maxAmount`, `confirmPassword === password`, `initialPayment >= propertyValue * 0.2`).
|
|
@@ -3310,6 +3408,8 @@ A: Схема — обычная функция, поэтому опиши ст
|
|
|
3310
3408
|
|
|
3311
3409
|
## 62. Purpose
|
|
3312
3410
|
|
|
3411
|
+
**Submit и Reset — Жизненный цикл отправки формы**
|
|
3412
|
+
|
|
3313
3413
|
Канонический submit-флоу под M1: «запустить полную валидацию данных (`validateModel`) →
|
|
3314
3414
|
проверить `boolean`-результат → достать снимок (`model.get()`) → сделать запрос → `model.reset()`».
|
|
3315
3415
|
Валидация — **отдельный слой** (`@reformer/core/validation`, схема через `defineValidationSchema`), она
|
|
@@ -3568,6 +3668,8 @@ A: `model.get()` возвращает значения всех полей. Фи
|
|
|
3568
3668
|
|
|
3569
3669
|
## 68. Purpose
|
|
3570
3670
|
|
|
3671
|
+
**Async Preload — Загрузка начальных значений и справочников**
|
|
3672
|
+
|
|
3571
3673
|
Загрузка формы данными с сервера под M1: (1) **initial values** в `createModel(...)`; (2)
|
|
3572
3674
|
**`model.set` / `model.patch`** для загрузки/обновления значений; (3) **external React-hook**
|
|
3573
3675
|
для full-blown async preload (параллельный fetch заявки + справочников, обработка ошибок,
|
|
@@ -3597,7 +3699,7 @@ Initial values задаются в `createModel(initial)`; `reset()` возвр
|
|
|
3597
3699
|
|
|
3598
3700
|
```typescript
|
|
3599
3701
|
import { createModel, createForm } from '@reformer/core';
|
|
3600
|
-
import { Input,
|
|
3702
|
+
import { Input, SelectAsync, CheckboxWithLabel } from '@reformer/ui-kit';
|
|
3601
3703
|
|
|
3602
3704
|
type ProfileForm = { username: string; language: 'ru' | 'en'; marketing: boolean };
|
|
3603
3705
|
|
|
@@ -3606,7 +3708,7 @@ const schema = {
|
|
|
3606
3708
|
username: { value: model.$.username, component: Input, componentProps: { label: 'Username' } },
|
|
3607
3709
|
language: {
|
|
3608
3710
|
value: model.$.language,
|
|
3609
|
-
component:
|
|
3711
|
+
component: SelectAsync,
|
|
3610
3712
|
componentProps: {
|
|
3611
3713
|
label: 'Язык',
|
|
3612
3714
|
options: [
|
|
@@ -3615,7 +3717,7 @@ const schema = {
|
|
|
3615
3717
|
],
|
|
3616
3718
|
},
|
|
3617
3719
|
},
|
|
3618
|
-
marketing: { value: model.$.marketing, component:
|
|
3720
|
+
marketing: { value: model.$.marketing, component: CheckboxWithLabel, componentProps: { label: 'Рассылка' } },
|
|
3619
3721
|
};
|
|
3620
3722
|
const form = createForm({ model, schema });
|
|
3621
3723
|
// model.get() === { username: '', language: 'ru', marketing: true }
|
|
@@ -3760,6 +3862,8 @@ A: `reset()` возвращает к initial-снимку из `createModel`, н
|
|
|
3760
3862
|
|
|
3761
3863
|
## 74. 30. TYPE-SAFETY RECIPES
|
|
3762
3864
|
|
|
3865
|
+
**type-safety-recipes**
|
|
3866
|
+
|
|
3763
3867
|
Идиоматичные паттерны, которые держат сгенерированный код без `any` и `as`-кастов под M1.
|
|
3764
3868
|
|
|
3765
3869
|
### Recipe 1 — Imports (root cause prevention)
|
|
@@ -3819,7 +3923,7 @@ Layout-схема БЕЗ валидаторов — правила живут в
|
|
|
3819
3923
|
const model = createModel<CreditApplicationForm>(initial);
|
|
3820
3924
|
|
|
3821
3925
|
const schema = {
|
|
3822
|
-
loanAmount: { value: model.$.loanAmount, component:
|
|
3926
|
+
loanAmount: { value: model.$.loanAmount, component: InputNumber },
|
|
3823
3927
|
// вложенная группа — builder, принимающий ModelSignals<Sub>
|
|
3824
3928
|
personalData: personalDataNodes(model.$.personalData),
|
|
3825
3929
|
// массив — { array, item }
|
|
@@ -3920,6 +4024,8 @@ validate(model.$.birthDate, [validateAdultAge]);
|
|
|
3920
4024
|
|
|
3921
4025
|
## 75. 31. ASYNC VALIDATOR
|
|
3922
4026
|
|
|
4027
|
+
**async-validator-debounce**
|
|
4028
|
+
|
|
3923
4029
|
Для проверок типа «уникальность email», «валидация ИНН через API», «проверка адреса» —
|
|
3924
4030
|
async-правило это `AsyncRule<T>` = `(value, { signal }) => Promise<ValidationError | null>`.
|
|
3925
4031
|
Оно регистрируется оператором `validateAsync(sig, [asyncRules])` внутри схемы валидации
|
|
@@ -4056,6 +4162,8 @@ return checking ? <Spinner /> : errors.length ? <Error errors={errors} /> : null
|
|
|
4056
4162
|
|
|
4057
4163
|
## 76. 32. ASYNC OPTIONS LOADING
|
|
4058
4164
|
|
|
4165
|
+
**async-options-loading**
|
|
4166
|
+
|
|
4059
4167
|
Для динамической подгрузки опций dropdown'а по значению другого поля (`region` → `city options`,
|
|
4060
4168
|
`carBrand` → `carModel options`) — используй `onChange` из `@reformer/core/behaviors` +
|
|
4061
4169
|
`form.field.updateComponentProps({ options })`. `onChange` даёт debounce и AbortSignal из коробки.
|
|
@@ -4165,6 +4273,8 @@ loadOptionsOn(model.$.carBrand, form.carModel, fetchCarModels, { resetTarget: tr
|
|
|
4165
4273
|
|
|
4166
4274
|
## 77. Purpose
|
|
4167
4275
|
|
|
4276
|
+
**useFormValidation — Единый выбор стратегии валидации**
|
|
4277
|
+
|
|
4168
4278
|
Одна декларативная точка выбора «когда прогонять схему валидации» вместо ручной разводки
|
|
4169
4279
|
`markAsTouched` + `validateModel` + `revalidateWhen` + `useState(pending)`. Аддитивный слой над
|
|
4170
4280
|
функциональной схемой (`@reformer/core/validation`): переиспользует `validateModel` как
|
|
@@ -4359,6 +4469,34 @@ if (typeof window !== 'undefined') ctrl.start();
|
|
|
4359
4469
|
|
|
4360
4470
|
_Auto-generated from JSDoc on public exports._
|
|
4361
4471
|
|
|
4472
|
+
### aggregateInto
|
|
4473
|
+
|
|
4474
|
+
**Kind:** `function`
|
|
4475
|
+
|
|
4476
|
+
Агрегатная запись в строки массива. `derive(snapshot)` получает СНИМОК строк и возвращает список
|
|
4477
|
+
`{ index, patch }`, который применяется к строкам. Записи КОАЛЕСИРУЮТСЯ в один отложенный проход на
|
|
4478
|
+
финальном состоянии — поэтому массовые синхронные мутации (push в цикле) не вызывают каскад на
|
|
4479
|
+
промежуточных состояниях. `derive` должна сходиться (на фикспоинте возвращать те же значения).
|
|
4480
|
+
|
|
4481
|
+
**Signature:**
|
|
4482
|
+
```typescript
|
|
4483
|
+
export function aggregateInto<TItem>(
|
|
4484
|
+
array: object,
|
|
4485
|
+
derive: (rows: TItem[]) => Array<{ index: number; patch: Partial<TItem> }>
|
|
4486
|
+
): void
|
|
4487
|
+
```
|
|
4488
|
+
|
|
4489
|
+
**Examples:**
|
|
4490
|
+
|
|
4491
|
+
// последняя строка = 100 − Σ(остальные)
|
|
4492
|
+
aggregateInto(model.$.rows, (rows) => {
|
|
4493
|
+
const n = rows.length; if (n === 0) return [];
|
|
4494
|
+
const others = rows.slice(0, n - 1).reduce((s, r) => s + r.percent, 0);
|
|
4495
|
+
return [{ index: n - 1, patch: { percent: 100 - others } }];
|
|
4496
|
+
});
|
|
4497
|
+
|
|
4498
|
+
_Source: src/form/behaviors/collections.ts_
|
|
4499
|
+
|
|
4362
4500
|
### AnyFunction
|
|
4363
4501
|
|
|
4364
4502
|
**Kind:** `type`
|
|
@@ -4373,6 +4511,47 @@ export type AnyFunction = (...args: never[]) => unknown;
|
|
|
4373
4511
|
|
|
4374
4512
|
_Source: src/form/types/index.ts_
|
|
4375
4513
|
|
|
4514
|
+
### apply
|
|
4515
|
+
|
|
4516
|
+
**Kind:** `function`
|
|
4517
|
+
|
|
4518
|
+
Композиция под-схем в текущую (над той же моделью scope). Заменяет пошаговую группировку.
|
|
4519
|
+
|
|
4520
|
+
**Signature:**
|
|
4521
|
+
```typescript
|
|
4522
|
+
export function apply<T>(...schemas: ValidationSchema<T>[]): void
|
|
4523
|
+
```
|
|
4524
|
+
|
|
4525
|
+
_Source: src/form/validation/operators.ts_
|
|
4526
|
+
|
|
4527
|
+
### applyEach
|
|
4528
|
+
|
|
4529
|
+
**Kind:** `function`
|
|
4530
|
+
|
|
4531
|
+
Применить под-схему к КАЖДОМУ элементу динамического массива (per-item поведение).
|
|
4532
|
+
Реагирует на добавление/удаление строк: новым строкам поведение применяется, удалённым — отписывается.
|
|
4533
|
+
|
|
4534
|
+
Под-схема получает scope строки: `model` (под-модель строки — `row.$.field`) и `form` (нода строки).
|
|
4535
|
+
- Value-операции (`compute`/`copyFrom`/`transformValue` на `row.$.*`) работают всегда.
|
|
4536
|
+
- Node-операции (`enableWhen`/`updateComponentProps`/`reset` через `form.*`) требуют, чтобы массив был
|
|
4537
|
+
МАТЕРИАЛИЗОВАН в форме (узел `{ array, item }` в схеме) — тогда `form` строки = та же нода, что
|
|
4538
|
+
рендерится, а её сигналы зарегистрированы (`enableWhen` резолвит ноду). Без материализации доступ
|
|
4539
|
+
к `form.*` бросит понятную ошибку (см. {@link unmaterializedRowForm}).
|
|
4540
|
+
|
|
4541
|
+
**Signature:**
|
|
4542
|
+
```typescript
|
|
4543
|
+
export function applyEach<TItem>(array: object, itemSchema: FormBehavior<TItem>): void
|
|
4544
|
+
```
|
|
4545
|
+
|
|
4546
|
+
**Examples:**
|
|
4547
|
+
|
|
4548
|
+
applyEach(model.$.items, defineFormBehavior<Item>(({ model: row, form }) => {
|
|
4549
|
+
compute(row.$.lineTotal, () => row.qty * row.price); // value-op — всегда
|
|
4550
|
+
enableWhen(row.$.discount, () => row.qty > 10); // node-op — нужна материализация массива
|
|
4551
|
+
}));
|
|
4552
|
+
|
|
4553
|
+
_Source: src/form/behaviors/collections.ts_
|
|
4554
|
+
|
|
4376
4555
|
### ArrayControlState
|
|
4377
4556
|
|
|
4378
4557
|
**Kind:** `interface`
|
|
@@ -4585,6 +4764,23 @@ export interface ArrayNodeLike {
|
|
|
4585
4764
|
|
|
4586
4765
|
_Source: src/form/types/index.ts_
|
|
4587
4766
|
|
|
4767
|
+
### AsyncRule
|
|
4768
|
+
|
|
4769
|
+
**Kind:** `type`
|
|
4770
|
+
|
|
4771
|
+
Асинхронное правило поля. Получает `AbortSignal` для отмены устаревших ответов (быстрый повторный
|
|
4772
|
+
прогон той же схемы отменяет предыдущий). Сетевой сбой не должен блокировать — ловите и возвращайте `null`.
|
|
4773
|
+
|
|
4774
|
+
**Signature:**
|
|
4775
|
+
```typescript
|
|
4776
|
+
export type AsyncRule<TField> = (
|
|
4777
|
+
value: TField,
|
|
4778
|
+
ctx: { signal: AbortSignal }
|
|
4779
|
+
) => Promise<ValidationError | null>;
|
|
4780
|
+
```
|
|
4781
|
+
|
|
4782
|
+
_Source: src/form/validation/types.ts_
|
|
4783
|
+
|
|
4588
4784
|
### AsyncValidatorFn
|
|
4589
4785
|
|
|
4590
4786
|
**Kind:** `type`
|
|
@@ -4657,6 +4853,48 @@ export type BehaviorCleanup = () => void;
|
|
|
4657
4853
|
|
|
4658
4854
|
_Source: src/model/behaviors-value.ts_
|
|
4659
4855
|
|
|
4856
|
+
### BehaviorScope
|
|
4857
|
+
|
|
4858
|
+
**Kind:** `type`
|
|
4859
|
+
|
|
4860
|
+
Контекст схемы поведения: модель (значения/сигналы) + форма (ноды).
|
|
4861
|
+
|
|
4862
|
+
**Signature:**
|
|
4863
|
+
```typescript
|
|
4864
|
+
export type BehaviorScope<T> = { model: FormModel<T>; form: FormProxy<T> };
|
|
4865
|
+
```
|
|
4866
|
+
|
|
4867
|
+
_Source: src/form/behaviors/types.ts_
|
|
4868
|
+
|
|
4869
|
+
### bindFieldProps
|
|
4870
|
+
|
|
4871
|
+
**Kind:** `function`
|
|
4872
|
+
|
|
4873
|
+
Props контрола под адаптер: срезает служебные ключи обёртки и `strip` адаптера, кладёт значение
|
|
4874
|
+
в `valueProp` (через `toValue`), вешает `changeProp`, который прогоняет эмит контрола через
|
|
4875
|
+
`fromEmit` в value-based `onChange` поля, и пробрасывает blur (`bindBlur` либо `onBlur`).
|
|
4876
|
+
Без адаптера seam проходит как есть (`value` / `onChange` / `onBlur`).
|
|
4877
|
+
|
|
4878
|
+
`componentProps` спредятся ПЕРВЫМИ — seam их перекрывает при совпадении ключей.
|
|
4879
|
+
|
|
4880
|
+
**Signature:**
|
|
4881
|
+
```typescript
|
|
4882
|
+
export function bindFieldProps(
|
|
4883
|
+
adapter: FieldAdapter | undefined,
|
|
4884
|
+
seam: FieldSeam,
|
|
4885
|
+
componentProps: Record<string, unknown> = {}
|
|
4886
|
+
): Record<string, unknown>
|
|
4887
|
+
```
|
|
4888
|
+
|
|
4889
|
+
**Parameters:**
|
|
4890
|
+
- `adapter` — - Адаптер контрола или `undefined` (value-based контрол).
|
|
4891
|
+
- `seam` — - Значение поля и его колбэки.
|
|
4892
|
+
- `componentProps` — - Прочие props контрола.
|
|
4893
|
+
|
|
4894
|
+
**Returns:** Плоский объект props для спреда в контрол.
|
|
4895
|
+
|
|
4896
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
4897
|
+
|
|
4660
4898
|
### buildValidation
|
|
4661
4899
|
|
|
4662
4900
|
**Kind:** `function`
|
|
@@ -4694,6 +4932,38 @@ await v!.validateAll(); // все шаги + extras
|
|
|
4694
4932
|
|
|
4695
4933
|
_Source: src/form/validation/config.ts_
|
|
4696
4934
|
|
|
4935
|
+
### ChangeContext
|
|
4936
|
+
|
|
4937
|
+
**Kind:** `interface`
|
|
4938
|
+
|
|
4939
|
+
Контекст async-реакции: `signal` аннулируется, когда поле меняется снова до завершения колбэка.
|
|
4940
|
+
|
|
4941
|
+
**Signature:**
|
|
4942
|
+
```typescript
|
|
4943
|
+
export interface ChangeContext {
|
|
4944
|
+
signal: AbortSignal;
|
|
4945
|
+
}
|
|
4946
|
+
```
|
|
4947
|
+
|
|
4948
|
+
_Source: src/form/behaviors/types.ts_
|
|
4949
|
+
|
|
4950
|
+
### compute
|
|
4951
|
+
|
|
4952
|
+
**Kind:** `function`
|
|
4953
|
+
|
|
4954
|
+
Вычисляемое поле с auto-tracking: `target = read()` при изменении прочитанных сигналов.
|
|
4955
|
+
|
|
4956
|
+
**Signature:**
|
|
4957
|
+
```typescript
|
|
4958
|
+
export function compute<R>(
|
|
4959
|
+
target: Signal<R>,
|
|
4960
|
+
read: () => R,
|
|
4961
|
+
options?: { when?: () => boolean }
|
|
4962
|
+
): void
|
|
4963
|
+
```
|
|
4964
|
+
|
|
4965
|
+
_Source: src/form/behaviors/operators.ts_
|
|
4966
|
+
|
|
4697
4967
|
### computeFrom
|
|
4698
4968
|
|
|
4699
4969
|
**Kind:** `function`
|
|
@@ -4976,7 +5246,7 @@ _Source: src/form/create-core-form.ts_
|
|
|
4976
5246
|
|
|
4977
5247
|
Значения принадлежат модели (источник истины), ноды формы держат UI/валидационное состояние и
|
|
4978
5248
|
ссылаются на сигналы модели по идентичности (`node.value === model.$.path`). Обходит структуру
|
|
4979
|
-
модели, привязывает конфиг поля (component/componentProps
|
|
5249
|
+
модели, привязывает конфиг поля (component/componentProps) из схемы, материализует
|
|
4980
5250
|
top-level массивы как {@link ModelArrayNode}, заполняет реестр сигнал→нода (для `enableWhen`/
|
|
4981
5251
|
роутинга ошибок) и, при наличии, запускает декларативное поведение (cleanup живёт на форме).
|
|
4982
5252
|
|
|
@@ -5008,7 +5278,8 @@ const model = createModel<Form>({ email: '', profile: { name: '', age: 0 } });
|
|
|
5008
5278
|
const schema = {
|
|
5009
5279
|
component: Section,
|
|
5010
5280
|
children: [
|
|
5011
|
-
|
|
5281
|
+
// Layout несёт только component/componentProps; правила — в отдельной ValidationSchema.
|
|
5282
|
+
{ value: model.$.email, component: Input },
|
|
5012
5283
|
// вложенная группа: `model.$.profile.name` (≡ под-модель `model.profile.$.name` — тот же сигнал)
|
|
5013
5284
|
{ value: model.$.profile.name, component: Input },
|
|
5014
5285
|
],
|
|
@@ -5031,7 +5302,7 @@ _Source: src/form/create-form.ts_
|
|
|
5031
5302
|
**Kind:** `interface`
|
|
5032
5303
|
|
|
5033
5304
|
Аргументы createForm под архитектуру M1: данные приходят из {@link FormModel},
|
|
5034
|
-
конфиг полей (component/componentProps
|
|
5305
|
+
конфиг полей (component/componentProps) — из единой схемы.
|
|
5035
5306
|
|
|
5036
5307
|
**Signature:**
|
|
5037
5308
|
```typescript
|
|
@@ -5053,6 +5324,41 @@ export interface CreateFormFromModelArgs<T> {
|
|
|
5053
5324
|
|
|
5054
5325
|
_Source: src/form/create-form.ts_
|
|
5055
5326
|
|
|
5327
|
+
### createFormValidation
|
|
5328
|
+
|
|
5329
|
+
**Kind:** `function`
|
|
5330
|
+
|
|
5331
|
+
Собрать контроллер валидации формы с выбранной стратегией запуска. Переиспользует
|
|
5332
|
+
{@link validateModel} как единственный движок прогона.
|
|
5333
|
+
|
|
5334
|
+
Фабрика ЧИСТАЯ до `start()` (никаких подписок) — безопасна для SSR/headless. Реактивные триггеры
|
|
5335
|
+
арминуются только в `start()` (в React — из `useEffect`).
|
|
5336
|
+
|
|
5337
|
+
**Signature:**
|
|
5338
|
+
```typescript
|
|
5339
|
+
export function createFormValidation<T>(
|
|
5340
|
+
model: FormModel<T>,
|
|
5341
|
+
schema: ValidationSchema<T>,
|
|
5342
|
+
options: ValidationStrategyOptions = {}
|
|
5343
|
+
): FormValidationController
|
|
5344
|
+
```
|
|
5345
|
+
|
|
5346
|
+
**Parameters:**
|
|
5347
|
+
- `model` — - Модель данных.
|
|
5348
|
+
- `schema` — - Схема валидации. **Стабильная ссылка** (иначе `validateModel` не отменит устаревший прогон).
|
|
5349
|
+
- `options` — - {@link ValidationStrategyOptions}.
|
|
5350
|
+
|
|
5351
|
+
**Examples:**
|
|
5352
|
+
|
|
5353
|
+
```ts
|
|
5354
|
+
const ctrl = createFormValidation(model, schema, { strategy: 'afterFirstSubmit', debounce: 300 });
|
|
5355
|
+
const dispose = ctrl.start(); // на клиенте
|
|
5356
|
+
const ok = await ctrl.validate(); // submit → раскрыть все ошибки
|
|
5357
|
+
dispose();
|
|
5358
|
+
```
|
|
5359
|
+
|
|
5360
|
+
_Source: src/form/validation/strategy.ts_
|
|
5361
|
+
|
|
5056
5362
|
### createModel
|
|
5057
5363
|
|
|
5058
5364
|
**Kind:** `function`
|
|
@@ -5089,6 +5395,81 @@ model.get(); // { email: 'a@b.c', profile: { name: 'Ada' }, tag
|
|
|
5089
5395
|
|
|
5090
5396
|
_Source: src/model/create-model.ts_
|
|
5091
5397
|
|
|
5398
|
+
### cross
|
|
5399
|
+
|
|
5400
|
+
**Kind:** `function`
|
|
5401
|
+
|
|
5402
|
+
Cross-field правило: `fn` получает СНАПШОТ модели текущего scope (`model.get()`) и вешает ошибку на `sig`.
|
|
5403
|
+
Для элементов массива / под-моделей захватывайте нужный снапшот в замыкание (`const item = im.get()`),
|
|
5404
|
+
т.к. `fn` всегда получает модель ТЕКУЩЕГО scope (корень прогона), а не под-модель.
|
|
5405
|
+
|
|
5406
|
+
**Signature:**
|
|
5407
|
+
```typescript
|
|
5408
|
+
export function cross<TSnapshot>(
|
|
5409
|
+
sig: PathAwareSignal<unknown>,
|
|
5410
|
+
fn: (form: TSnapshot) => ValidationError | null
|
|
5411
|
+
): void
|
|
5412
|
+
```
|
|
5413
|
+
|
|
5414
|
+
_Source: src/form/validation/operators.ts_
|
|
5415
|
+
|
|
5416
|
+
### defer
|
|
5417
|
+
|
|
5418
|
+
**Kind:** `function`
|
|
5419
|
+
|
|
5420
|
+
Отложенная запись вне effect-контекста (микротаск) — защита от «Cycle detected».
|
|
5421
|
+
|
|
5422
|
+
**Signature:**
|
|
5423
|
+
```typescript
|
|
5424
|
+
export function defer(fn: () => void): void
|
|
5425
|
+
```
|
|
5426
|
+
|
|
5427
|
+
_Source: src/form/behaviors/context.ts_
|
|
5428
|
+
|
|
5429
|
+
### defineFormBehavior
|
|
5430
|
+
|
|
5431
|
+
**Kind:** `function`
|
|
5432
|
+
|
|
5433
|
+
Описать поведение формы декларативно. Возвращает {@link FormBehavior} для `createForm({ behavior })`.
|
|
5434
|
+
|
|
5435
|
+
**Signature:**
|
|
5436
|
+
```typescript
|
|
5437
|
+
export function defineFormBehavior<T>(setup: (scope: BehaviorScope<T>) => void): FormBehavior<T>
|
|
5438
|
+
```
|
|
5439
|
+
|
|
5440
|
+
**Examples:**
|
|
5441
|
+
|
|
5442
|
+
```ts
|
|
5443
|
+
export const myBehavior = defineFormBehavior<MyForm>(({ model, form }) => {
|
|
5444
|
+
compute(model.$.total, () => model.price * model.qty);
|
|
5445
|
+
enableWhen([model.$.city], () => Boolean(model.country));
|
|
5446
|
+
onChange(model.$.country, async (c) => form.city.updateComponentProps({ options: await load(c) }));
|
|
5447
|
+
});
|
|
5448
|
+
```
|
|
5449
|
+
|
|
5450
|
+
_Source: src/form/behaviors/context.ts_
|
|
5451
|
+
|
|
5452
|
+
### defineValidationSchema
|
|
5453
|
+
|
|
5454
|
+
**Kind:** `function`
|
|
5455
|
+
|
|
5456
|
+
Тонкая identity-обёртка для типизации/discoverability (как `defineFormBehavior`). Возвращает схему как есть.
|
|
5457
|
+
|
|
5458
|
+
**Signature:**
|
|
5459
|
+
```typescript
|
|
5460
|
+
export function defineValidationSchema<T>(schema: ValidationSchema<T>): ValidationSchema<T>
|
|
5461
|
+
```
|
|
5462
|
+
|
|
5463
|
+
**Examples:**
|
|
5464
|
+
|
|
5465
|
+
```ts
|
|
5466
|
+
export const step1 = defineValidationSchema<LoanForm>(({ model }) => {
|
|
5467
|
+
validate(model.$.loanAmount, [required(), min(50000)]);
|
|
5468
|
+
});
|
|
5469
|
+
```
|
|
5470
|
+
|
|
5471
|
+
_Source: src/form/validation/run.ts_
|
|
5472
|
+
|
|
5092
5473
|
### disableWhen
|
|
5093
5474
|
|
|
5094
5475
|
**Kind:** `function`
|
|
@@ -5121,6 +5502,23 @@ disableWhen(model.$.discount, () => model.plan !== 'promo', { resetOnDisable: tr
|
|
|
5121
5502
|
|
|
5122
5503
|
_Source: src/form/behaviors/node.ts_
|
|
5123
5504
|
|
|
5505
|
+
### each
|
|
5506
|
+
|
|
5507
|
+
**Kind:** `function`
|
|
5508
|
+
|
|
5509
|
+
Применить под-правила к КАЖДОМУ элементу текущего массива модели.
|
|
5510
|
+
`U extends object` — элементы должны быть под-моделями (объектами); для массива примитивов валидируйте лист напрямую.
|
|
5511
|
+
|
|
5512
|
+
**Signature:**
|
|
5513
|
+
```typescript
|
|
5514
|
+
export function each<U extends object>(
|
|
5515
|
+
arr: ModelArray<U>,
|
|
5516
|
+
itemFn: (item: FormModel<U>) => void
|
|
5517
|
+
): void
|
|
5518
|
+
```
|
|
5519
|
+
|
|
5520
|
+
_Source: src/form/validation/operators.ts_
|
|
5521
|
+
|
|
5124
5522
|
### eachLeafSignal
|
|
5125
5523
|
|
|
5126
5524
|
**Kind:** `function`
|
|
@@ -5142,6 +5540,55 @@ export function eachLeafSignal<T>(
|
|
|
5142
5540
|
|
|
5143
5541
|
_Source: src/model/create-model.ts_
|
|
5144
5542
|
|
|
5543
|
+
### effect
|
|
5544
|
+
|
|
5545
|
+
**Kind:** `function`
|
|
5546
|
+
|
|
5547
|
+
Реактивный эффект (авто-dispose). Колбэк может вернуть собственный cleanup.
|
|
5548
|
+
|
|
5549
|
+
**Signature:**
|
|
5550
|
+
```typescript
|
|
5551
|
+
export function effect(fn: () => void | (() => void)): void
|
|
5552
|
+
```
|
|
5553
|
+
|
|
5554
|
+
_Source: src/form/behaviors/context.ts_
|
|
5555
|
+
|
|
5556
|
+
### email
|
|
5557
|
+
|
|
5558
|
+
**Kind:** `function`
|
|
5559
|
+
|
|
5560
|
+
Фабрика валидатора формата email.
|
|
5561
|
+
|
|
5562
|
+
Проверяет по упрощённому regex `^[^\s@]+@[^\s@]+\.[^\s@]+$`. Пустые значения
|
|
5563
|
+
(`''`/`null`/`undefined`) пропускаются (используйте {@link required} для обязательности).
|
|
5564
|
+
|
|
5565
|
+
**Signature:**
|
|
5566
|
+
```typescript
|
|
5567
|
+
export function email<TForm = unknown, TField extends string | null | undefined = string>(
|
|
5568
|
+
options?: ValidateOptions
|
|
5569
|
+
): Validator<TForm, TField>
|
|
5570
|
+
```
|
|
5571
|
+
|
|
5572
|
+
**Parameters:**
|
|
5573
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
5574
|
+
|
|
5575
|
+
**Returns:** Чистый валидатор {@link Validator} для строкового поля
|
|
5576
|
+
|
|
5577
|
+
**Examples:**
|
|
5578
|
+
|
|
5579
|
+
Проверка формата email
|
|
5580
|
+
```typescript
|
|
5581
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
5582
|
+
import { required, email } from '@reformer/core/validators';
|
|
5583
|
+
|
|
5584
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
5585
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
5586
|
+
validate(model.$.email, [required(), email({ message: 'Введите корректный email' })]);
|
|
5587
|
+
});
|
|
5588
|
+
```
|
|
5589
|
+
|
|
5590
|
+
_Source: src/form/validators/email.ts_
|
|
5591
|
+
|
|
5145
5592
|
### enableWhen
|
|
5146
5593
|
|
|
5147
5594
|
**Kind:** `function`
|
|
@@ -5244,20 +5691,119 @@ export enum ErrorStrategy {
|
|
|
5244
5691
|
|
|
5245
5692
|
_Source: src/form/validation/error-handler.ts_
|
|
5246
5693
|
|
|
5247
|
-
###
|
|
5694
|
+
### exclusiveFlag
|
|
5248
5695
|
|
|
5249
|
-
**Kind:** `
|
|
5696
|
+
**Kind:** `function`
|
|
5250
5697
|
|
|
5251
|
-
|
|
5698
|
+
Взаимное исключение булева флага среди строк массива (single-selection — «единственный primary»).
|
|
5699
|
+
Когда флаг строки становится true, у остальных строк он сбрасывается в false. Не хрупок к push:
|
|
5700
|
+
новые строки с флагом false исключения не запускают.
|
|
5252
5701
|
|
|
5253
5702
|
**Signature:**
|
|
5254
5703
|
```typescript
|
|
5255
|
-
export
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5704
|
+
export function exclusiveFlag<TItem>(
|
|
5705
|
+
array: object,
|
|
5706
|
+
getFlag: (row: FormModel<TItem>) => Signal<boolean>
|
|
5707
|
+
): void
|
|
5708
|
+
```
|
|
5709
|
+
|
|
5710
|
+
**Examples:**
|
|
5711
|
+
|
|
5712
|
+
exclusiveFlag(model.$.contacts, (row) => row.$.primary);
|
|
5713
|
+
|
|
5714
|
+
_Source: src/form/behaviors/collections.ts_
|
|
5715
|
+
|
|
5716
|
+
### FIELD_WRAPPER_ONLY_PROPS
|
|
5717
|
+
|
|
5718
|
+
**Kind:** `const`
|
|
5719
|
+
|
|
5720
|
+
Props, которые адресованы обёртке поля, а не контролу: `labelTooltip` рисует `FormField` у
|
|
5721
|
+
подписи. Они приходят в общем мешке `componentProps`, и без среза утекли бы в DOM контрола.
|
|
5722
|
+
|
|
5723
|
+
**Signature:**
|
|
5724
|
+
```typescript
|
|
5725
|
+
export const FIELD_WRAPPER_ONLY_PROPS: readonly string[]
|
|
5726
|
+
```
|
|
5727
|
+
|
|
5728
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
5729
|
+
|
|
5730
|
+
### FieldAdapter
|
|
5731
|
+
|
|
5732
|
+
**Kind:** `interface`
|
|
5733
|
+
|
|
5734
|
+
Адаптер поля: описывает диалект контрола (`checked`/`onCheckedChange`, `value`/`onValueChange`,
|
|
5735
|
+
DOM-событие в `onChange`, …). Все ключи необязательны — по умолчанию seam проходит как есть.
|
|
5736
|
+
|
|
5737
|
+
**Signature:**
|
|
5738
|
+
```typescript
|
|
5739
|
+
export interface FieldAdapter {
|
|
5740
|
+
/** Проп, из которого контрол читает значение (по умолчанию `'value'`). */
|
|
5741
|
+
valueProp?: string;
|
|
5742
|
+
/** Колбэк, через который контрол эмитит изменение (по умолчанию `'onChange'`). */
|
|
5743
|
+
changeProp?: string;
|
|
5744
|
+
/**
|
|
5745
|
+
* Эмит контрола → значение поля (по умолчанию — как есть). `rest` — прочие props контрола
|
|
5746
|
+
* (например, чтобы достать `options` при резолве значения).
|
|
5747
|
+
*/
|
|
5748
|
+
fromEmit?: (arg: unknown, rest: Record<string, unknown>) => unknown;
|
|
5749
|
+
/** Значение поля → `valueProp` контрола (коэрсия `null`/`undefined`; по умолчанию — как есть). */
|
|
5750
|
+
toValue?: (value: unknown) => unknown;
|
|
5751
|
+
/** Проброс blur нестандартным каналом (по умолчанию — `onBlur`). */
|
|
5752
|
+
bindBlur?: (onBlur: () => void) => Record<string, unknown>;
|
|
5753
|
+
/** Ключи, которые убрать из `componentProps` перед спредом в контрол. */
|
|
5754
|
+
strip?: string[];
|
|
5755
|
+
/**
|
|
5756
|
+
* Передавать ли контролу ноду формы пропом `control` (только рендерер). По умолчанию `false`:
|
|
5757
|
+
* errors/touched обслуживает обёртка поля. Ставь `true`, если контрол сам вызывает
|
|
5758
|
+
* `useFormControl(control)`.
|
|
5759
|
+
*/
|
|
5760
|
+
passControl?: boolean;
|
|
5761
|
+
}
|
|
5762
|
+
```
|
|
5763
|
+
|
|
5764
|
+
**Examples:**
|
|
5765
|
+
|
|
5766
|
+
Radix Checkbox
|
|
5767
|
+
```ts
|
|
5768
|
+
const checkedAdapter: FieldAdapter = {
|
|
5769
|
+
valueProp: 'checked',
|
|
5770
|
+
changeProp: 'onCheckedChange',
|
|
5771
|
+
fromEmit: (c) => c === true,
|
|
5772
|
+
toValue: (v) => v ?? false,
|
|
5773
|
+
};
|
|
5774
|
+
```
|
|
5775
|
+
|
|
5776
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
5777
|
+
|
|
5778
|
+
### FieldAdapterCarrier
|
|
5779
|
+
|
|
5780
|
+
**Kind:** `interface`
|
|
5781
|
+
|
|
5782
|
+
Компонент, объявивший свой диалект статикой.
|
|
5783
|
+
|
|
5784
|
+
**Signature:**
|
|
5785
|
+
```typescript
|
|
5786
|
+
export interface FieldAdapterCarrier {
|
|
5787
|
+
reformerAdapter?: FieldAdapter;
|
|
5788
|
+
}
|
|
5789
|
+
```
|
|
5790
|
+
|
|
5791
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
5792
|
+
|
|
5793
|
+
### FieldConfig
|
|
5794
|
+
|
|
5795
|
+
**Kind:** `interface`
|
|
5796
|
+
|
|
5797
|
+
Конфигурация поля
|
|
5798
|
+
|
|
5799
|
+
**Signature:**
|
|
5800
|
+
```typescript
|
|
5801
|
+
export interface FieldConfig<T> {
|
|
5802
|
+
/**
|
|
5803
|
+
* Начальное значение-литерал (legacy-путь). Под архитектурой M1 значение приходит из
|
|
5804
|
+
* {@link FieldConfig.valueSignal} (сигнал {@link FormModel}); тогда `value` не требуется.
|
|
5805
|
+
*/
|
|
5806
|
+
value?: T | null;
|
|
5261
5807
|
/**
|
|
5262
5808
|
* Сигнал значения из {@link FormModel} (M1). Если задан — служит источником истины значения
|
|
5263
5809
|
* поля (нода не владеет значением, а ссылается на этот сигнал). Имеет приоритет над `value`.
|
|
@@ -5506,6 +6052,38 @@ onChange={e => control.setValue(e.target.value)}
|
|
|
5506
6052
|
|
|
5507
6053
|
_Source: src/platforms/react/hooks/types.ts_
|
|
5508
6054
|
|
|
6055
|
+
### FieldHandle
|
|
6056
|
+
|
|
6057
|
+
**Kind:** `interface`
|
|
6058
|
+
|
|
6059
|
+
Базовый императивный контракт поля формы.
|
|
6060
|
+
|
|
6061
|
+
Обёртка поля (`FormField.Control` из `@reformer/cdk`, рендерер `@reformer/renderer-react`)
|
|
6062
|
+
строит его из DOM-узла контрола. Композиты со своим handle (`open`/`close`/`reload`/…) отдают
|
|
6063
|
+
его как есть — он расширяет этот контракт.
|
|
6064
|
+
|
|
6065
|
+
Достаётся из render-схемы по селектору:
|
|
6066
|
+
`schema.node(sel).getRef<FieldHandle>().current?.focus()`.
|
|
6067
|
+
|
|
6068
|
+
Покрывает ТОЛЬКО императивные действия. Реактивное состояние (value / disabled / options /
|
|
6069
|
+
validation) живёт в behaviors и через handle не дублируется.
|
|
6070
|
+
|
|
6071
|
+
**Signature:**
|
|
6072
|
+
```typescript
|
|
6073
|
+
export interface FieldHandle {
|
|
6074
|
+
/** Сфокусировать поле (делегирует на DOM-элемент контрола). */
|
|
6075
|
+
focus(): void;
|
|
6076
|
+
/** Снять фокус. */
|
|
6077
|
+
blur(): void;
|
|
6078
|
+
/** Проскроллить поле в область видимости. */
|
|
6079
|
+
scrollIntoView(opts?: ScrollIntoViewOptions): void;
|
|
6080
|
+
/** Живой DOM-элемент поля (или `null` до монтирования / для размонтированной ноды). */
|
|
6081
|
+
getElement(): HTMLElement | null;
|
|
6082
|
+
}
|
|
6083
|
+
```
|
|
6084
|
+
|
|
6085
|
+
_Source: src/platforms/react/field-binding/field-handle.ts_
|
|
6086
|
+
|
|
5509
6087
|
### FieldNode
|
|
5510
6088
|
|
|
5511
6089
|
**Kind:** `class`
|
|
@@ -5522,6 +6100,7 @@ export class FieldNode<T> extends FormNode<T> {
|
|
|
5522
6100
|
|
|
5523
6101
|
**Examples:**
|
|
5524
6102
|
|
|
6103
|
+
Пример node-level: конфиг УЗЛА, а не layout-схема M1 (в ней поля `validators` нет).
|
|
5525
6104
|
```typescript
|
|
5526
6105
|
const field = new FieldNode({
|
|
5527
6106
|
value: '',
|
|
@@ -5536,6 +6115,23 @@ console.log(field.valid.value); // true
|
|
|
5536
6115
|
|
|
5537
6116
|
_Source: src/form/nodes/field-node.ts_
|
|
5538
6117
|
|
|
6118
|
+
### FieldSeam
|
|
6119
|
+
|
|
6120
|
+
**Kind:** `interface`
|
|
6121
|
+
|
|
6122
|
+
Seam поля: текущее значение и value-based колбэки.
|
|
6123
|
+
|
|
6124
|
+
**Signature:**
|
|
6125
|
+
```typescript
|
|
6126
|
+
export interface FieldSeam {
|
|
6127
|
+
value: unknown;
|
|
6128
|
+
onChange: (value: unknown) => void;
|
|
6129
|
+
onBlur: () => void;
|
|
6130
|
+
}
|
|
6131
|
+
```
|
|
6132
|
+
|
|
6133
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
6134
|
+
|
|
5539
6135
|
### FieldStatus
|
|
5540
6136
|
|
|
5541
6137
|
**Kind:** `type`
|
|
@@ -5549,6 +6145,69 @@ export type FieldStatus = 'valid' | 'invalid' | 'pending' | 'disabled';
|
|
|
5549
6145
|
|
|
5550
6146
|
_Source: src/form/types/contracts.ts_
|
|
5551
6147
|
|
|
6148
|
+
### FileLike
|
|
6149
|
+
|
|
6150
|
+
**Kind:** `interface`
|
|
6151
|
+
|
|
6152
|
+
Минимальный файлоподобный контракт, с которым работают file-валидаторы:
|
|
6153
|
+
нативный `File`, сериализуемый дескриптор загруженного файла и т.п.
|
|
6154
|
+
|
|
6155
|
+
**Signature:**
|
|
6156
|
+
```typescript
|
|
6157
|
+
export interface FileLike {
|
|
6158
|
+
name: string;
|
|
6159
|
+
size?: number;
|
|
6160
|
+
type?: string;
|
|
6161
|
+
}
|
|
6162
|
+
```
|
|
6163
|
+
|
|
6164
|
+
_Source: src/form/validators/file-utils.ts_
|
|
6165
|
+
|
|
6166
|
+
### fileType
|
|
6167
|
+
|
|
6168
|
+
**Kind:** `function`
|
|
6169
|
+
|
|
6170
|
+
Фабрика валидатора типа файла.
|
|
6171
|
+
|
|
6172
|
+
Проверяет каждый файл против accept-строки в синтаксисе нативного атрибута
|
|
6173
|
+
`<input type="file" accept>`: расширения (`.pdf`), точные MIME (`image/png`),
|
|
6174
|
+
wildcard-категории (`image/*`), список через запятую. Матчинг регистронезависимый,
|
|
6175
|
+
по `type` и/или расширению из `name` ({@link matchesFileAccept}).
|
|
6176
|
+
|
|
6177
|
+
Нужен потому, что нативный `accept` — только подсказка пикеру: на drag-and-drop
|
|
6178
|
+
и «All files» он не действует. Пустые значения (`null`/`undefined`/`''`/`[]`)
|
|
6179
|
+
пропускаются (используйте {@link required} для обязательности).
|
|
6180
|
+
|
|
6181
|
+
**Signature:**
|
|
6182
|
+
```typescript
|
|
6183
|
+
export function fileType<TForm = unknown, TField = unknown>(
|
|
6184
|
+
accept: string,
|
|
6185
|
+
options?: ValidateOptions
|
|
6186
|
+
): Validator<TForm, TField>
|
|
6187
|
+
```
|
|
6188
|
+
|
|
6189
|
+
**Parameters:**
|
|
6190
|
+
- `accept` — - Accept-строка допустимых типов, например `'image/*,.pdf'`
|
|
6191
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
6192
|
+
попадают `accept` и `fileName` (первый нарушивший файл).
|
|
6193
|
+
|
|
6194
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
6195
|
+
|
|
6196
|
+
**Examples:**
|
|
6197
|
+
|
|
6198
|
+
Только изображения и PDF
|
|
6199
|
+
```typescript
|
|
6200
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
6201
|
+
import { fileType } from '@reformer/core/validators';
|
|
6202
|
+
|
|
6203
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
6204
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
6205
|
+
validate(model.$.documents, [fileType('image/*,.pdf', { message: 'Только изображения или PDF' })]);
|
|
6206
|
+
});
|
|
6207
|
+
```
|
|
6208
|
+
|
|
6209
|
+
_Source: src/form/validators/file-type.ts_
|
|
6210
|
+
|
|
5552
6211
|
### FormArrayProxy
|
|
5553
6212
|
|
|
5554
6213
|
**Kind:** `type`
|
|
@@ -5603,6 +6262,22 @@ todos.forEach((item, i) => {
|
|
|
5603
6262
|
|
|
5604
6263
|
_Source: src/form/types/form-proxy.ts_
|
|
5605
6264
|
|
|
6265
|
+
### FormBehavior
|
|
6266
|
+
|
|
6267
|
+
**Kind:** `interface`
|
|
6268
|
+
|
|
6269
|
+
Результат {@link defineFormBehavior}; передаётся в `createForm({ behavior })`.
|
|
6270
|
+
|
|
6271
|
+
**Signature:**
|
|
6272
|
+
```typescript
|
|
6273
|
+
export interface FormBehavior<T> {
|
|
6274
|
+
/** @internal Запускается `createForm` после построения нод и заполнения реестра сигнал→нода. */
|
|
6275
|
+
__run(model: FormModel<T>, form: FormProxy<T>): BehaviorCleanup;
|
|
6276
|
+
}
|
|
6277
|
+
```
|
|
6278
|
+
|
|
6279
|
+
_Source: src/form/behaviors/types.ts_
|
|
6280
|
+
|
|
5606
6281
|
### FormBundleLike
|
|
5607
6282
|
|
|
5608
6283
|
**Kind:** `interface`
|
|
@@ -6074,6 +6749,37 @@ export interface FormValidationBundle<T> {
|
|
|
6074
6749
|
|
|
6075
6750
|
_Source: src/form/validation/config.ts_
|
|
6076
6751
|
|
|
6752
|
+
### FormValidationController
|
|
6753
|
+
|
|
6754
|
+
**Kind:** `interface`
|
|
6755
|
+
|
|
6756
|
+
**Signature:**
|
|
6757
|
+
```typescript
|
|
6758
|
+
export interface FormValidationController {
|
|
6759
|
+
/**
|
|
6760
|
+
* Полный прогон схемы с раскрытием ошибок (`touch: true`) — для submit. Также переводит
|
|
6761
|
+
* `afterFirstSubmit` в live-фазу. Возвращает `false`, если есть блокирующие ошибки.
|
|
6762
|
+
*/
|
|
6763
|
+
validate(): Promise<boolean>;
|
|
6764
|
+
/**
|
|
6765
|
+
* Армировать реактивные подписки стратегии. Идемпотентно. Возвращает `dispose`. НЕ звать при SSR.
|
|
6766
|
+
*
|
|
6767
|
+
* После {@link FormValidationController.dispose} контроллер армируется ЗАНОВО: React монтирует
|
|
6768
|
+
* компоненты повторно (StrictMode в разработке, remount по роуту), и «одноразовый» контроллер
|
|
6769
|
+
* после такого цикла молча оставался бы без живой валидации.
|
|
6770
|
+
*/
|
|
6771
|
+
start(): () => void;
|
|
6772
|
+
/** Снять подписки/таймеры. Идемпотентно; после него `start()` снова армирует стратегию. */
|
|
6773
|
+
dispose(): void;
|
|
6774
|
+
/** Идёт ли прогон (submit или live) — снапшот. */
|
|
6775
|
+
readonly isValidating: boolean;
|
|
6776
|
+
/** Реактивный сигнал состояния прогона (для тонкой подписки в UI). */
|
|
6777
|
+
readonly validating: ReadonlySignal<boolean>;
|
|
6778
|
+
}
|
|
6779
|
+
```
|
|
6780
|
+
|
|
6781
|
+
_Source: src/form/validation/strategy.ts_
|
|
6782
|
+
|
|
6077
6783
|
### FormValue
|
|
6078
6784
|
|
|
6079
6785
|
**Kind:** `type`
|
|
@@ -6097,6 +6803,63 @@ export type FormValue =
|
|
|
6097
6803
|
|
|
6098
6804
|
_Source: src/form/types/contracts.ts_
|
|
6099
6805
|
|
|
6806
|
+
### futureDate
|
|
6807
|
+
|
|
6808
|
+
**Kind:** `function`
|
|
6809
|
+
|
|
6810
|
+
Фабрика валидатора, проверяющего что дата не в прошлом.
|
|
6811
|
+
|
|
6812
|
+
Дата не должна быть раньше сегодняшнего дня (сравнение по нормализованным датам).
|
|
6813
|
+
Пустые и невалидные даты пропускаются (используйте {@link required} и {@link isDate}).
|
|
6814
|
+
|
|
6815
|
+
**Signature:**
|
|
6816
|
+
```typescript
|
|
6817
|
+
export function futureDate<
|
|
6818
|
+
TForm = unknown,
|
|
6819
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
6820
|
+
>(options?: ValidateOptions): Validator<TForm, TField>
|
|
6821
|
+
```
|
|
6822
|
+
|
|
6823
|
+
**Parameters:**
|
|
6824
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
6825
|
+
|
|
6826
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты (`string | Date`)
|
|
6827
|
+
|
|
6828
|
+
**Examples:**
|
|
6829
|
+
|
|
6830
|
+
Дата не в прошлом
|
|
6831
|
+
```typescript
|
|
6832
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
6833
|
+
import { required, futureDate } from '@reformer/core/validators';
|
|
6834
|
+
|
|
6835
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
6836
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
6837
|
+
validate(model.$.appointmentDate, [required(), futureDate({ message: 'Дата записи должна быть в будущем' })]);
|
|
6838
|
+
});
|
|
6839
|
+
```
|
|
6840
|
+
|
|
6841
|
+
_Source: src/form/validators/future-date.ts_
|
|
6842
|
+
|
|
6843
|
+
### getFieldAdapter
|
|
6844
|
+
|
|
6845
|
+
**Kind:** `function`
|
|
6846
|
+
|
|
6847
|
+
Адаптер, объявленный компонентом статикой `reformerAdapter`, либо `undefined`.
|
|
6848
|
+
|
|
6849
|
+
**Signature:**
|
|
6850
|
+
```typescript
|
|
6851
|
+
export function getFieldAdapter(component: unknown): FieldAdapter | undefined
|
|
6852
|
+
```
|
|
6853
|
+
|
|
6854
|
+
**Examples:**
|
|
6855
|
+
|
|
6856
|
+
```ts
|
|
6857
|
+
Checkbox.reformerAdapter = checkedAdapter;
|
|
6858
|
+
getFieldAdapter(Checkbox); // → checkedAdapter
|
|
6859
|
+
```
|
|
6860
|
+
|
|
6861
|
+
_Source: src/platforms/react/field-binding/field-adapter.ts_
|
|
6862
|
+
|
|
6100
6863
|
### getNodeForSignal
|
|
6101
6864
|
|
|
6102
6865
|
**Kind:** `function`
|
|
@@ -6158,6 +6921,19 @@ console.log('Node type:', getNodeType(node)); // "FieldNode" | "GroupNode" | "Ar
|
|
|
6158
6921
|
|
|
6159
6922
|
_Source: src/form/type-guards.ts_
|
|
6160
6923
|
|
|
6924
|
+
### getScope
|
|
6925
|
+
|
|
6926
|
+
**Kind:** `function`
|
|
6927
|
+
|
|
6928
|
+
Текущий scope ({ model, form }) активной схемы (escape hatch для кросс-операторов).
|
|
6929
|
+
|
|
6930
|
+
**Signature:**
|
|
6931
|
+
```typescript
|
|
6932
|
+
export function getScope<T>(): BehaviorScope<T>
|
|
6933
|
+
```
|
|
6934
|
+
|
|
6935
|
+
_Source: src/form/behaviors/context.ts_
|
|
6936
|
+
|
|
6161
6937
|
### GroupNode
|
|
6162
6938
|
|
|
6163
6939
|
**Kind:** `class`
|
|
@@ -6212,6 +6988,41 @@ export interface GroupNodeConfig<T> {
|
|
|
6212
6988
|
|
|
6213
6989
|
_Source: src/form/types/index.ts_
|
|
6214
6990
|
|
|
6991
|
+
### integer
|
|
6992
|
+
|
|
6993
|
+
**Kind:** `function`
|
|
6994
|
+
|
|
6995
|
+
Фабрика валидатора, проверяющего что число — целое.
|
|
6996
|
+
|
|
6997
|
+
Пустые значения и не-числа пропускаются (используйте {@link required} и {@link isNumber}).
|
|
6998
|
+
|
|
6999
|
+
**Signature:**
|
|
7000
|
+
```typescript
|
|
7001
|
+
export function integer<TForm = unknown, TField extends number | null | undefined = number>(
|
|
7002
|
+
options?: ValidateOptions
|
|
7003
|
+
): Validator<TForm, TField>
|
|
7004
|
+
```
|
|
7005
|
+
|
|
7006
|
+
**Parameters:**
|
|
7007
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
7008
|
+
|
|
7009
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
7010
|
+
|
|
7011
|
+
**Examples:**
|
|
7012
|
+
|
|
7013
|
+
Проверка целого числа
|
|
7014
|
+
```typescript
|
|
7015
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7016
|
+
import { required, integer } from '@reformer/core/validators';
|
|
7017
|
+
|
|
7018
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7019
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7020
|
+
validate(model.$.count, [required(), integer({ message: 'Должно быть целым числом' })]);
|
|
7021
|
+
});
|
|
7022
|
+
```
|
|
7023
|
+
|
|
7024
|
+
_Source: src/form/validators/integer.ts_
|
|
7025
|
+
|
|
6215
7026
|
### isArrayNode
|
|
6216
7027
|
|
|
6217
7028
|
**Kind:** `function`
|
|
@@ -6243,6 +7054,43 @@ if (isArrayNode(node)) {
|
|
|
6243
7054
|
|
|
6244
7055
|
_Source: src/form/type-guards.ts_
|
|
6245
7056
|
|
|
7057
|
+
### isDate
|
|
7058
|
+
|
|
7059
|
+
**Kind:** `function`
|
|
7060
|
+
|
|
7061
|
+
Фабрика валидатора, проверяющего что значение — валидная дата.
|
|
7062
|
+
|
|
7063
|
+
Принимает `Date` или строку, парсимую в дату. Пустые значения (`''`/`null`/`undefined`)
|
|
7064
|
+
пропускаются (используйте {@link required} для обязательности).
|
|
7065
|
+
|
|
7066
|
+
**Signature:**
|
|
7067
|
+
```typescript
|
|
7068
|
+
export function isDate<
|
|
7069
|
+
TForm = unknown,
|
|
7070
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
7071
|
+
>(options?: ValidateOptions): Validator<TForm, TField>
|
|
7072
|
+
```
|
|
7073
|
+
|
|
7074
|
+
**Parameters:**
|
|
7075
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
7076
|
+
|
|
7077
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты (`string | Date`)
|
|
7078
|
+
|
|
7079
|
+
**Examples:**
|
|
7080
|
+
|
|
7081
|
+
Проверка валидности даты
|
|
7082
|
+
```typescript
|
|
7083
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7084
|
+
import { required, isDate } from '@reformer/core/validators';
|
|
7085
|
+
|
|
7086
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7087
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7088
|
+
validate(model.$.eventDate, [required(), isDate({ message: 'Введите корректную дату' })]);
|
|
7089
|
+
});
|
|
7090
|
+
```
|
|
7091
|
+
|
|
7092
|
+
_Source: src/form/validators/is-date.ts_
|
|
7093
|
+
|
|
6246
7094
|
### isDerived
|
|
6247
7095
|
|
|
6248
7096
|
**Kind:** `function`
|
|
@@ -6308,6 +7156,25 @@ if (isFieldNode(node)) {
|
|
|
6308
7156
|
|
|
6309
7157
|
_Source: src/form/type-guards.ts_
|
|
6310
7158
|
|
|
7159
|
+
### isFileLike
|
|
7160
|
+
|
|
7161
|
+
**Kind:** `function`
|
|
7162
|
+
|
|
7163
|
+
Проверяет, что значение файлоподобно: объект со строковым `name` и опциональными
|
|
7164
|
+
числовым `size` / строковым `type`.
|
|
7165
|
+
|
|
7166
|
+
**Signature:**
|
|
7167
|
+
```typescript
|
|
7168
|
+
export function isFileLike(value: unknown): value is FileLike
|
|
7169
|
+
```
|
|
7170
|
+
|
|
7171
|
+
**Parameters:**
|
|
7172
|
+
- `value` — - Проверяемое значение
|
|
7173
|
+
|
|
7174
|
+
**Returns:** `true`, если значение соответствует {@link FileLike}
|
|
7175
|
+
|
|
7176
|
+
_Source: src/form/validators/file-utils.ts_
|
|
7177
|
+
|
|
6311
7178
|
### isFormNode
|
|
6312
7179
|
|
|
6313
7180
|
**Kind:** `function`
|
|
@@ -6389,31 +7256,82 @@ const isLeaf = (v: unknown) => isSignalLike(v) && !isModelContainerSignal(v);
|
|
|
6389
7256
|
|
|
6390
7257
|
_Source: src/model/model-signals-proxy.ts_
|
|
6391
7258
|
|
|
6392
|
-
###
|
|
7259
|
+
### isNumber
|
|
6393
7260
|
|
|
6394
7261
|
**Kind:** `function`
|
|
6395
7262
|
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
Вызывается операторами {@link computeFrom}/`compute` для их сигнала-цели. После этого
|
|
6399
|
-
bulk-сеттеры (`model.set`/`model.patch`, `patchValue`/`setValue`) пропускают это поле, чтобы
|
|
6400
|
-
значение из payload не затирало вычисляемое. Прикладной код напрямую обычно не вызывает.
|
|
7263
|
+
Фабрика валидатора, проверяющего что значение — конечное число (не NaN, не строка).
|
|
6401
7264
|
|
|
6402
|
-
|
|
6403
|
-
|
|
7265
|
+
Пустые значения (`null`/`undefined`) пропускаются (используйте {@link required} для
|
|
7266
|
+
обязательности). В отличие от других number-валидаторов, **не** пропускает не-числа
|
|
7267
|
+
и `NaN` — это его задача.
|
|
6404
7268
|
|
|
6405
7269
|
**Signature:**
|
|
6406
7270
|
```typescript
|
|
6407
|
-
export function
|
|
7271
|
+
export function isNumber<TForm = unknown, TField extends number | null | undefined = number>(
|
|
7272
|
+
options?: ValidateOptions
|
|
7273
|
+
): Validator<TForm, TField>
|
|
6408
7274
|
```
|
|
6409
7275
|
|
|
6410
7276
|
**Parameters:**
|
|
6411
|
-
- `
|
|
7277
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
7278
|
+
|
|
7279
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
6412
7280
|
|
|
6413
7281
|
**Examples:**
|
|
6414
7282
|
|
|
7283
|
+
Проверка, что значение — число
|
|
6415
7284
|
```typescript
|
|
6416
|
-
import {
|
|
7285
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7286
|
+
import { required, isNumber } from '@reformer/core/validators';
|
|
7287
|
+
|
|
7288
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7289
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7290
|
+
validate(model.$.amount, [required(), isNumber({ message: 'Введите число' })]);
|
|
7291
|
+
});
|
|
7292
|
+
```
|
|
7293
|
+
|
|
7294
|
+
_Source: src/form/validators/is-number.ts_
|
|
7295
|
+
|
|
7296
|
+
### makeElementFieldHandle
|
|
7297
|
+
|
|
7298
|
+
**Kind:** `function`
|
|
7299
|
+
|
|
7300
|
+
Собирает базовый {@link FieldHandle}, делегирующий на DOM-элемент по ссылке `el`.
|
|
7301
|
+
Все вызовы null-safe: до монтирования (`el.current === null`) — no-op, без исключений.
|
|
7302
|
+
|
|
7303
|
+
**Signature:**
|
|
7304
|
+
```typescript
|
|
7305
|
+
export function makeElementFieldHandle(el: RefObject<HTMLElement | null>): FieldHandle
|
|
7306
|
+
```
|
|
7307
|
+
|
|
7308
|
+
_Source: src/platforms/react/field-binding/field-handle.ts_
|
|
7309
|
+
|
|
7310
|
+
### markDerived
|
|
7311
|
+
|
|
7312
|
+
**Kind:** `function`
|
|
7313
|
+
|
|
7314
|
+
Пометить сигнал производным (вычисляемым).
|
|
7315
|
+
|
|
7316
|
+
Вызывается операторами {@link computeFrom}/`compute` для их сигнала-цели. После этого
|
|
7317
|
+
bulk-сеттеры (`model.set`/`model.patch`, `patchValue`/`setValue`) пропускают это поле, чтобы
|
|
7318
|
+
значение из payload не затирало вычисляемое. Прикладной код напрямую обычно не вызывает.
|
|
7319
|
+
|
|
7320
|
+
Идемпотентна на уровне флага: повторные вызовы наращивают счётчик ссылок, чтобы каждый оператор,
|
|
7321
|
+
пишущий в этот сигнал, был сбалансирован своим {@link unmarkDerived} при dispose.
|
|
7322
|
+
|
|
7323
|
+
**Signature:**
|
|
7324
|
+
```typescript
|
|
7325
|
+
export function markDerived(signal: Signal<any>): void
|
|
7326
|
+
```
|
|
7327
|
+
|
|
7328
|
+
**Parameters:**
|
|
7329
|
+
- `signal` — - Сигнал значения из {@link FormModel}, которым владеет compute
|
|
7330
|
+
|
|
7331
|
+
**Examples:**
|
|
7332
|
+
|
|
7333
|
+
```typescript
|
|
7334
|
+
import { markDerived } from '@reformer/core';
|
|
6417
7335
|
|
|
6418
7336
|
// Помечаем поле total как вычисляемое — bulk-set его не перезапишет
|
|
6419
7337
|
markDerived(model.$.total);
|
|
@@ -6425,6 +7343,550 @@ markDerived(model.$.total);
|
|
|
6425
7343
|
|
|
6426
7344
|
_Source: src/model/derived-registry.ts_
|
|
6427
7345
|
|
|
7346
|
+
### matchesFileAccept
|
|
7347
|
+
|
|
7348
|
+
**Kind:** `function`
|
|
7349
|
+
|
|
7350
|
+
Проверяет файл против accept-паттерна в синтаксисе нативного атрибута
|
|
7351
|
+
`<input type="file" accept>`: список через запятую из расширений (`.pdf`),
|
|
7352
|
+
точных MIME (`image/png`) и wildcard-категорий (`image/*`). Регистронезависимо.
|
|
7353
|
+
|
|
7354
|
+
Нужен собственный матчер, потому что `accept` у нативного input — только подсказка
|
|
7355
|
+
пикеру: на drag-and-drop и на программное значение он не действует.
|
|
7356
|
+
|
|
7357
|
+
**Signature:**
|
|
7358
|
+
```typescript
|
|
7359
|
+
export function matchesFileAccept(file: Pick<FileLike, 'name' | 'type'>, accept: string): boolean
|
|
7360
|
+
```
|
|
7361
|
+
|
|
7362
|
+
**Parameters:**
|
|
7363
|
+
- `file` — - Файлоподобное значение (`name` + опциональный `type`)
|
|
7364
|
+
- `accept` — - Accept-строка; пустая/пробельная строка матчит всё
|
|
7365
|
+
|
|
7366
|
+
**Returns:** `true`, если файл подходит хотя бы под один паттерн
|
|
7367
|
+
|
|
7368
|
+
_Source: src/form/validators/file-utils.ts_
|
|
7369
|
+
|
|
7370
|
+
### max
|
|
7371
|
+
|
|
7372
|
+
**Kind:** `function`
|
|
7373
|
+
|
|
7374
|
+
Фабрика валидатора максимального числового значения.
|
|
7375
|
+
|
|
7376
|
+
Пустые значения (`null`/`undefined`) пропускаются (используйте {@link required} для обязательности).
|
|
7377
|
+
|
|
7378
|
+
**Signature:**
|
|
7379
|
+
```typescript
|
|
7380
|
+
export function max<TForm = unknown, TField extends number | null | undefined = number>(
|
|
7381
|
+
maxValue: number,
|
|
7382
|
+
options?: ValidateOptions
|
|
7383
|
+
): Validator<TForm, TField>
|
|
7384
|
+
```
|
|
7385
|
+
|
|
7386
|
+
**Parameters:**
|
|
7387
|
+
- `maxValue` — - Максимально допустимое значение (включительно)
|
|
7388
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7389
|
+
попадают `max` и `actual`.
|
|
7390
|
+
|
|
7391
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
7392
|
+
|
|
7393
|
+
**Examples:**
|
|
7394
|
+
|
|
7395
|
+
Максимальное значение числового поля
|
|
7396
|
+
```typescript
|
|
7397
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7398
|
+
import { required, max } from '@reformer/core/validators';
|
|
7399
|
+
|
|
7400
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7401
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7402
|
+
validate(model.$.quantity, [max(100)]);
|
|
7403
|
+
validate(model.$.discount, [required(), max(50, { message: 'Не более 50%' })]);
|
|
7404
|
+
});
|
|
7405
|
+
```
|
|
7406
|
+
|
|
7407
|
+
_Source: src/form/validators/max.ts_
|
|
7408
|
+
|
|
7409
|
+
### maxAge
|
|
7410
|
+
|
|
7411
|
+
**Kind:** `function`
|
|
7412
|
+
|
|
7413
|
+
Фабрика валидатора максимального возраста (по дате рождения).
|
|
7414
|
+
|
|
7415
|
+
Возраст вычисляется по дате рождения относительно сегодняшнего дня. Пустые и невалидные
|
|
7416
|
+
даты пропускаются (используйте {@link required} и {@link isDate}).
|
|
7417
|
+
|
|
7418
|
+
**Signature:**
|
|
7419
|
+
```typescript
|
|
7420
|
+
export function maxAge<
|
|
7421
|
+
TForm = unknown,
|
|
7422
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
7423
|
+
>(maxAgeValue: number, options?: ValidateOptions): Validator<TForm, TField>
|
|
7424
|
+
```
|
|
7425
|
+
|
|
7426
|
+
**Parameters:**
|
|
7427
|
+
- `maxAgeValue` — - Максимально допустимый возраст (в полных годах)
|
|
7428
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7429
|
+
попадают `maxAge` и `currentAge`.
|
|
7430
|
+
|
|
7431
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты рождения (`string | Date`)
|
|
7432
|
+
|
|
7433
|
+
**Examples:**
|
|
7434
|
+
|
|
7435
|
+
Максимальный возраст
|
|
7436
|
+
```typescript
|
|
7437
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7438
|
+
import { maxAge } from '@reformer/core/validators';
|
|
7439
|
+
|
|
7440
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7441
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7442
|
+
validate(model.$.birthDate, [maxAge(100, { message: 'Проверьте дату рождения' })]);
|
|
7443
|
+
});
|
|
7444
|
+
```
|
|
7445
|
+
|
|
7446
|
+
_Source: src/form/validators/max-age.ts_
|
|
7447
|
+
|
|
7448
|
+
### maxDate
|
|
7449
|
+
|
|
7450
|
+
**Kind:** `function`
|
|
7451
|
+
|
|
7452
|
+
Фабрика валидатора максимальной даты (включительно).
|
|
7453
|
+
|
|
7454
|
+
Сравнение по нормализованным датам (время обнуляется). Пустые и невалидные даты
|
|
7455
|
+
пропускаются (используйте {@link required} и {@link isDate}).
|
|
7456
|
+
|
|
7457
|
+
**Signature:**
|
|
7458
|
+
```typescript
|
|
7459
|
+
export function maxDate<
|
|
7460
|
+
TForm = unknown,
|
|
7461
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
7462
|
+
>(maxDateValue: Date, options?: ValidateOptions): Validator<TForm, TField>
|
|
7463
|
+
```
|
|
7464
|
+
|
|
7465
|
+
**Parameters:**
|
|
7466
|
+
- `maxDateValue` — - Максимально допустимая дата (включительно)
|
|
7467
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7468
|
+
попадает `maxDate`.
|
|
7469
|
+
|
|
7470
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты (`string | Date`)
|
|
7471
|
+
|
|
7472
|
+
**Examples:**
|
|
7473
|
+
|
|
7474
|
+
Максимальная дата
|
|
7475
|
+
```typescript
|
|
7476
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7477
|
+
import { maxDate } from '@reformer/core/validators';
|
|
7478
|
+
|
|
7479
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7480
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7481
|
+
validate(model.$.birthDate, [maxDate(new Date(), { message: 'Дата не может быть в будущем' })]);
|
|
7482
|
+
});
|
|
7483
|
+
```
|
|
7484
|
+
|
|
7485
|
+
_Source: src/form/validators/max-date.ts_
|
|
7486
|
+
|
|
7487
|
+
### maxFiles
|
|
7488
|
+
|
|
7489
|
+
**Kind:** `function`
|
|
7490
|
+
|
|
7491
|
+
Фабрика валидатора максимального количества файлов.
|
|
7492
|
+
|
|
7493
|
+
Работает с массивом файлов (одиночный файл считается как 1). Пустые значения
|
|
7494
|
+
(`null`/`undefined`/`''`/`[]`) пропускаются (используйте {@link required}
|
|
7495
|
+
для обязательности).
|
|
7496
|
+
|
|
7497
|
+
**Signature:**
|
|
7498
|
+
```typescript
|
|
7499
|
+
export function maxFiles<TForm = unknown, TField = unknown>(
|
|
7500
|
+
max: number,
|
|
7501
|
+
options?: ValidateOptions
|
|
7502
|
+
): Validator<TForm, TField>
|
|
7503
|
+
```
|
|
7504
|
+
|
|
7505
|
+
**Parameters:**
|
|
7506
|
+
- `max` — - Максимально допустимое количество файлов (включительно)
|
|
7507
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7508
|
+
попадают `maxFiles` и `actualCount`.
|
|
7509
|
+
|
|
7510
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
7511
|
+
|
|
7512
|
+
**Examples:**
|
|
7513
|
+
|
|
7514
|
+
Не более трёх вложений
|
|
7515
|
+
```typescript
|
|
7516
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7517
|
+
import { maxFiles } from '@reformer/core/validators';
|
|
7518
|
+
|
|
7519
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7520
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7521
|
+
validate(model.$.documents, [maxFiles(3, { message: 'Максимум 3 файла' })]);
|
|
7522
|
+
});
|
|
7523
|
+
```
|
|
7524
|
+
|
|
7525
|
+
_Source: src/form/validators/max-files.ts_
|
|
7526
|
+
|
|
7527
|
+
### maxFileSize
|
|
7528
|
+
|
|
7529
|
+
**Kind:** `function`
|
|
7530
|
+
|
|
7531
|
+
Фабрика валидатора максимального размера файла.
|
|
7532
|
+
|
|
7533
|
+
Работает с одиночным файлом или массивом (`File` либо файлоподобный дескриптор с
|
|
7534
|
+
`name`/`size`). Проверяется каждый файл; в ошибку попадает первый нарушивший.
|
|
7535
|
+
Пустые значения (`null`/`undefined`/`''`/`[]`) и элементы без числового `size`
|
|
7536
|
+
(например, дескриптор загруженного файла без размера) пропускаются
|
|
7537
|
+
(используйте {@link required} для обязательности).
|
|
7538
|
+
|
|
7539
|
+
**Signature:**
|
|
7540
|
+
```typescript
|
|
7541
|
+
export function maxFileSize<TForm = unknown, TField = unknown>(
|
|
7542
|
+
maxSize: number,
|
|
7543
|
+
options?: ValidateOptions
|
|
7544
|
+
): Validator<TForm, TField>
|
|
7545
|
+
```
|
|
7546
|
+
|
|
7547
|
+
**Parameters:**
|
|
7548
|
+
- `maxSize` — - Максимально допустимый размер файла в байтах (включительно)
|
|
7549
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7550
|
+
попадают `maxFileSize`, `fileName` и `actualSize`.
|
|
7551
|
+
|
|
7552
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
7553
|
+
|
|
7554
|
+
**Examples:**
|
|
7555
|
+
|
|
7556
|
+
Ограничение размера вложений
|
|
7557
|
+
```typescript
|
|
7558
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7559
|
+
import { maxFileSize } from '@reformer/core/validators';
|
|
7560
|
+
|
|
7561
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7562
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7563
|
+
validate(model.$.documents, [maxFileSize(5 * 1024 * 1024, { message: 'Файл больше 5 МБ' })]);
|
|
7564
|
+
});
|
|
7565
|
+
```
|
|
7566
|
+
|
|
7567
|
+
_Source: src/form/validators/max-file-size.ts_
|
|
7568
|
+
|
|
7569
|
+
### maxLength
|
|
7570
|
+
|
|
7571
|
+
**Kind:** `function`
|
|
7572
|
+
|
|
7573
|
+
Фабрика валидатора максимальной длины строки или массива.
|
|
7574
|
+
|
|
7575
|
+
Работает со строкой или массивом (проверяется `value.length`). Пустые значения
|
|
7576
|
+
(`null`/`undefined`/`''`) и значения без числового `length` пропускаются
|
|
7577
|
+
(используйте {@link required} для обязательности).
|
|
7578
|
+
|
|
7579
|
+
**Signature:**
|
|
7580
|
+
```typescript
|
|
7581
|
+
export function maxLength<TForm = unknown, TField = unknown>(
|
|
7582
|
+
maxLen: number,
|
|
7583
|
+
options?: ValidateOptions
|
|
7584
|
+
): Validator<TForm, TField>
|
|
7585
|
+
```
|
|
7586
|
+
|
|
7587
|
+
**Parameters:**
|
|
7588
|
+
- `maxLen` — - Максимально допустимая длина (включительно)
|
|
7589
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7590
|
+
попадают `maxLength` и `actualLength`.
|
|
7591
|
+
|
|
7592
|
+
**Returns:** Чистый валидатор {@link Validator} для строки или массива
|
|
7593
|
+
|
|
7594
|
+
**Examples:**
|
|
7595
|
+
|
|
7596
|
+
Максимальная длина строки
|
|
7597
|
+
```typescript
|
|
7598
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7599
|
+
import { maxLength } from '@reformer/core/validators';
|
|
7600
|
+
|
|
7601
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7602
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7603
|
+
validate(model.$.name, [maxLength(50)]);
|
|
7604
|
+
validate(model.$.bio, [maxLength(500, { message: 'Максимум 500 символов' })]);
|
|
7605
|
+
});
|
|
7606
|
+
```
|
|
7607
|
+
|
|
7608
|
+
_Source: src/form/validators/max-length.ts_
|
|
7609
|
+
|
|
7610
|
+
### maxTotalFileSize
|
|
7611
|
+
|
|
7612
|
+
**Kind:** `function`
|
|
7613
|
+
|
|
7614
|
+
Фабрика валидатора суммарного размера всех файлов.
|
|
7615
|
+
|
|
7616
|
+
Складывает `size` всех файлов значения (элементы без числового `size` в сумму
|
|
7617
|
+
не входят). Пустые значения (`null`/`undefined`/`''`/`[]`) пропускаются
|
|
7618
|
+
(используйте {@link required} для обязательности).
|
|
7619
|
+
|
|
7620
|
+
**Signature:**
|
|
7621
|
+
```typescript
|
|
7622
|
+
export function maxTotalFileSize<TForm = unknown, TField = unknown>(
|
|
7623
|
+
maxTotal: number,
|
|
7624
|
+
options?: ValidateOptions
|
|
7625
|
+
): Validator<TForm, TField>
|
|
7626
|
+
```
|
|
7627
|
+
|
|
7628
|
+
**Parameters:**
|
|
7629
|
+
- `maxTotal` — - Максимально допустимый суммарный размер в байтах (включительно)
|
|
7630
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7631
|
+
попадают `maxTotalFileSize` и `actualTotal`.
|
|
7632
|
+
|
|
7633
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
7634
|
+
|
|
7635
|
+
**Examples:**
|
|
7636
|
+
|
|
7637
|
+
Суммарно не более 20 МБ
|
|
7638
|
+
```typescript
|
|
7639
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7640
|
+
import { maxTotalFileSize } from '@reformer/core/validators';
|
|
7641
|
+
|
|
7642
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7643
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7644
|
+
validate(model.$.documents, [maxTotalFileSize(20 * 1024 * 1024, { message: 'Суммарно не более 20 МБ' })]);
|
|
7645
|
+
});
|
|
7646
|
+
```
|
|
7647
|
+
|
|
7648
|
+
_Source: src/form/validators/max-total-file-size.ts_
|
|
7649
|
+
|
|
7650
|
+
### min
|
|
7651
|
+
|
|
7652
|
+
**Kind:** `function`
|
|
7653
|
+
|
|
7654
|
+
Фабрика валидатора минимального числового значения.
|
|
7655
|
+
|
|
7656
|
+
Пустые значения (`null`/`undefined`) пропускаются (используйте {@link required} для обязательности).
|
|
7657
|
+
|
|
7658
|
+
**Signature:**
|
|
7659
|
+
```typescript
|
|
7660
|
+
export function min<TForm = unknown, TField extends number | null | undefined = number>(
|
|
7661
|
+
minValue: number,
|
|
7662
|
+
options?: ValidateOptions
|
|
7663
|
+
): Validator<TForm, TField>
|
|
7664
|
+
```
|
|
7665
|
+
|
|
7666
|
+
**Parameters:**
|
|
7667
|
+
- `minValue` — - Минимально допустимое значение (включительно)
|
|
7668
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7669
|
+
попадают `min` и `actual`.
|
|
7670
|
+
|
|
7671
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
7672
|
+
|
|
7673
|
+
**Examples:**
|
|
7674
|
+
|
|
7675
|
+
Минимальное значение числового поля
|
|
7676
|
+
```typescript
|
|
7677
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7678
|
+
import { required, min } from '@reformer/core/validators';
|
|
7679
|
+
|
|
7680
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7681
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7682
|
+
validate(model.$.age, [min(18)]);
|
|
7683
|
+
validate(model.$.quantity, [required(), min(1, { message: 'Минимум 1' })]);
|
|
7684
|
+
});
|
|
7685
|
+
```
|
|
7686
|
+
|
|
7687
|
+
_Source: src/form/validators/min.ts_
|
|
7688
|
+
|
|
7689
|
+
### minAge
|
|
7690
|
+
|
|
7691
|
+
**Kind:** `function`
|
|
7692
|
+
|
|
7693
|
+
Фабрика валидатора минимального возраста (по дате рождения).
|
|
7694
|
+
|
|
7695
|
+
Возраст вычисляется по дате рождения относительно сегодняшнего дня. Пустые и невалидные
|
|
7696
|
+
даты пропускаются (используйте {@link required} и {@link isDate}).
|
|
7697
|
+
|
|
7698
|
+
**Signature:**
|
|
7699
|
+
```typescript
|
|
7700
|
+
export function minAge<
|
|
7701
|
+
TForm = unknown,
|
|
7702
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
7703
|
+
>(minAgeValue: number, options?: ValidateOptions): Validator<TForm, TField>
|
|
7704
|
+
```
|
|
7705
|
+
|
|
7706
|
+
**Parameters:**
|
|
7707
|
+
- `minAgeValue` — - Минимально допустимый возраст (в полных годах)
|
|
7708
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7709
|
+
попадают `minAge` и `currentAge`.
|
|
7710
|
+
|
|
7711
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты рождения (`string | Date`)
|
|
7712
|
+
|
|
7713
|
+
**Examples:**
|
|
7714
|
+
|
|
7715
|
+
Минимальный возраст
|
|
7716
|
+
```typescript
|
|
7717
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7718
|
+
import { required, minAge } from '@reformer/core/validators';
|
|
7719
|
+
|
|
7720
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7721
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7722
|
+
validate(model.$.birthDate, [required(), minAge(18, { message: 'Вам должно быть не менее 18 лет' })]);
|
|
7723
|
+
});
|
|
7724
|
+
```
|
|
7725
|
+
|
|
7726
|
+
_Source: src/form/validators/min-age.ts_
|
|
7727
|
+
|
|
7728
|
+
### minDate
|
|
7729
|
+
|
|
7730
|
+
**Kind:** `function`
|
|
7731
|
+
|
|
7732
|
+
Фабрика валидатора минимальной даты (включительно).
|
|
7733
|
+
|
|
7734
|
+
Сравнение по нормализованным датам (время обнуляется). Пустые и невалидные даты
|
|
7735
|
+
пропускаются (используйте {@link required} и {@link isDate}).
|
|
7736
|
+
|
|
7737
|
+
**Signature:**
|
|
7738
|
+
```typescript
|
|
7739
|
+
export function minDate<
|
|
7740
|
+
TForm = unknown,
|
|
7741
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
7742
|
+
>(minDateValue: Date, options?: ValidateOptions): Validator<TForm, TField>
|
|
7743
|
+
```
|
|
7744
|
+
|
|
7745
|
+
**Parameters:**
|
|
7746
|
+
- `minDateValue` — - Минимально допустимая дата (включительно)
|
|
7747
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7748
|
+
попадает `minDate`.
|
|
7749
|
+
|
|
7750
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты (`string | Date`)
|
|
7751
|
+
|
|
7752
|
+
**Examples:**
|
|
7753
|
+
|
|
7754
|
+
Минимальная дата
|
|
7755
|
+
```typescript
|
|
7756
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7757
|
+
import { required, minDate } from '@reformer/core/validators';
|
|
7758
|
+
|
|
7759
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7760
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7761
|
+
validate(model.$.startDate, [required(), minDate(new Date(), { message: 'Дата не раньше сегодня' })]);
|
|
7762
|
+
});
|
|
7763
|
+
```
|
|
7764
|
+
|
|
7765
|
+
_Source: src/form/validators/min-date.ts_
|
|
7766
|
+
|
|
7767
|
+
### minFiles
|
|
7768
|
+
|
|
7769
|
+
**Kind:** `function`
|
|
7770
|
+
|
|
7771
|
+
Фабрика валидатора минимального количества файлов.
|
|
7772
|
+
|
|
7773
|
+
Работает с массивом файлов (одиночный файл считается как 1). Пустые значения
|
|
7774
|
+
(`null`/`undefined`/`''`/`[]`) пропускаются — «нужен хотя бы один файл» выражается
|
|
7775
|
+
связкой `required()` + `minFiles(n)`, как и у {@link minLength}.
|
|
7776
|
+
|
|
7777
|
+
**Signature:**
|
|
7778
|
+
```typescript
|
|
7779
|
+
export function minFiles<TForm = unknown, TField = unknown>(
|
|
7780
|
+
min: number,
|
|
7781
|
+
options?: ValidateOptions
|
|
7782
|
+
): Validator<TForm, TField>
|
|
7783
|
+
```
|
|
7784
|
+
|
|
7785
|
+
**Parameters:**
|
|
7786
|
+
- `min` — - Минимально допустимое количество файлов (включительно)
|
|
7787
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7788
|
+
попадают `minFiles` и `actualCount`.
|
|
7789
|
+
|
|
7790
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
7791
|
+
|
|
7792
|
+
**Examples:**
|
|
7793
|
+
|
|
7794
|
+
Не менее двух документов
|
|
7795
|
+
```typescript
|
|
7796
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7797
|
+
import { required, minFiles } from '@reformer/core/validators';
|
|
7798
|
+
|
|
7799
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7800
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7801
|
+
validate(model.$.documents, [required(), minFiles(2, { message: 'Приложите минимум 2 документа' })]);
|
|
7802
|
+
});
|
|
7803
|
+
```
|
|
7804
|
+
|
|
7805
|
+
_Source: src/form/validators/min-files.ts_
|
|
7806
|
+
|
|
7807
|
+
### minFileSize
|
|
7808
|
+
|
|
7809
|
+
**Kind:** `function`
|
|
7810
|
+
|
|
7811
|
+
Фабрика валидатора минимального размера файла.
|
|
7812
|
+
|
|
7813
|
+
Работает с одиночным файлом или массивом (`File` либо файлоподобный дескриптор с
|
|
7814
|
+
`name`/`size`). Проверяется каждый файл; в ошибку попадает первый нарушивший.
|
|
7815
|
+
Типовой сценарий — отсев пустых (0-байтовых) файлов: `minFileSize(1)`.
|
|
7816
|
+
Пустые значения (`null`/`undefined`/`''`/`[]`) и элементы без числового `size`
|
|
7817
|
+
пропускаются (используйте {@link required} для обязательности).
|
|
7818
|
+
|
|
7819
|
+
**Signature:**
|
|
7820
|
+
```typescript
|
|
7821
|
+
export function minFileSize<TForm = unknown, TField = unknown>(
|
|
7822
|
+
minSize: number,
|
|
7823
|
+
options?: ValidateOptions
|
|
7824
|
+
): Validator<TForm, TField>
|
|
7825
|
+
```
|
|
7826
|
+
|
|
7827
|
+
**Parameters:**
|
|
7828
|
+
- `minSize` — - Минимально допустимый размер файла в байтах (включительно)
|
|
7829
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7830
|
+
попадают `minFileSize`, `fileName` и `actualSize`.
|
|
7831
|
+
|
|
7832
|
+
**Returns:** Чистый валидатор {@link Validator} для файла или массива файлов
|
|
7833
|
+
|
|
7834
|
+
**Examples:**
|
|
7835
|
+
|
|
7836
|
+
Отсев пустых файлов
|
|
7837
|
+
```typescript
|
|
7838
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7839
|
+
import { minFileSize } from '@reformer/core/validators';
|
|
7840
|
+
|
|
7841
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7842
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7843
|
+
validate(model.$.documents, [minFileSize(1, { message: 'Файл пустой' })]);
|
|
7844
|
+
});
|
|
7845
|
+
```
|
|
7846
|
+
|
|
7847
|
+
_Source: src/form/validators/min-file-size.ts_
|
|
7848
|
+
|
|
7849
|
+
### minLength
|
|
7850
|
+
|
|
7851
|
+
**Kind:** `function`
|
|
7852
|
+
|
|
7853
|
+
Фабрика валидатора минимальной длины строки или массива.
|
|
7854
|
+
|
|
7855
|
+
Работает со строкой или массивом (проверяется `value.length`). Пустые значения
|
|
7856
|
+
(`null`/`undefined`/`''`) и значения без числового `length` пропускаются
|
|
7857
|
+
(используйте {@link required} для обязательности).
|
|
7858
|
+
|
|
7859
|
+
**Signature:**
|
|
7860
|
+
```typescript
|
|
7861
|
+
export function minLength<TForm = unknown, TField = unknown>(
|
|
7862
|
+
minLen: number,
|
|
7863
|
+
options?: ValidateOptions
|
|
7864
|
+
): Validator<TForm, TField>
|
|
7865
|
+
```
|
|
7866
|
+
|
|
7867
|
+
**Parameters:**
|
|
7868
|
+
- `minLen` — - Минимально допустимая длина (включительно)
|
|
7869
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
7870
|
+
попадают `minLength` и `actualLength`.
|
|
7871
|
+
|
|
7872
|
+
**Returns:** Чистый валидатор {@link Validator} для строки или массива
|
|
7873
|
+
|
|
7874
|
+
**Examples:**
|
|
7875
|
+
|
|
7876
|
+
Минимальная длина строки
|
|
7877
|
+
```typescript
|
|
7878
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
7879
|
+
import { required, minLength } from '@reformer/core/validators';
|
|
7880
|
+
|
|
7881
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
7882
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
7883
|
+
validate(model.$.name, [minLength(2)]);
|
|
7884
|
+
validate(model.$.password, [required(), minLength(8, { message: 'Минимум 8 символов' })]);
|
|
7885
|
+
});
|
|
7886
|
+
```
|
|
7887
|
+
|
|
7888
|
+
_Source: src/form/validators/min-length.ts_
|
|
7889
|
+
|
|
6428
7890
|
### ModelApi
|
|
6429
7891
|
|
|
6430
7892
|
**Kind:** `interface`
|
|
@@ -6678,6 +8140,44 @@ export type ModelValue<V> =
|
|
|
6678
8140
|
|
|
6679
8141
|
_Source: src/model/types.ts_
|
|
6680
8142
|
|
|
8143
|
+
### multipleOf
|
|
8144
|
+
|
|
8145
|
+
**Kind:** `function`
|
|
8146
|
+
|
|
8147
|
+
Фабрика валидатора, проверяющего что число кратно заданному.
|
|
8148
|
+
|
|
8149
|
+
Пустые значения и не-числа пропускаются (используйте {@link required} и {@link isNumber}).
|
|
8150
|
+
|
|
8151
|
+
**Signature:**
|
|
8152
|
+
```typescript
|
|
8153
|
+
export function multipleOf<TForm = unknown, TField extends number | null | undefined = number>(
|
|
8154
|
+
divisor: number,
|
|
8155
|
+
options?: ValidateOptions
|
|
8156
|
+
): Validator<TForm, TField>
|
|
8157
|
+
```
|
|
8158
|
+
|
|
8159
|
+
**Parameters:**
|
|
8160
|
+
- `divisor` — - Делитель: значение должно быть кратно ему
|
|
8161
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
8162
|
+
попадает `multipleOf` (делитель).
|
|
8163
|
+
|
|
8164
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
8165
|
+
|
|
8166
|
+
**Examples:**
|
|
8167
|
+
|
|
8168
|
+
Проверка кратности
|
|
8169
|
+
```typescript
|
|
8170
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8171
|
+
import { multipleOf } from '@reformer/core/validators';
|
|
8172
|
+
|
|
8173
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8174
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8175
|
+
validate(model.$.rating, [multipleOf(0.5, { message: 'Только шаг 0.5' })]);
|
|
8176
|
+
});
|
|
8177
|
+
```
|
|
8178
|
+
|
|
8179
|
+
_Source: src/form/validators/multiple-of.ts_
|
|
8180
|
+
|
|
6681
8181
|
### NodeFactory
|
|
6682
8182
|
|
|
6683
8183
|
**Kind:** `class`
|
|
@@ -6708,6 +8208,7 @@ export class NodeFactory {
|
|
|
6708
8208
|
* ```typescript
|
|
6709
8209
|
* const factory = new NodeFactory();
|
|
6710
8210
|
*
|
|
8211
|
+
* Конфиг node-level (не layout-схема M1, где `validators` уже нет).
|
|
6711
8212
|
* // FieldNode
|
|
6712
8213
|
* const field = factory.createNode({
|
|
6713
8214
|
* value: 'test@mail.com',
|
|
@@ -6740,13 +8241,157 @@ export class NodeFactory {
|
|
|
6740
8241
|
**Examples:**
|
|
6741
8242
|
|
|
6742
8243
|
```typescript
|
|
6743
|
-
import { NodeFactory } from '@reformer/core';
|
|
8244
|
+
import { NodeFactory } from '@reformer/core';
|
|
8245
|
+
|
|
8246
|
+
const factory = new NodeFactory();
|
|
8247
|
+
const node = factory.createNode({ value: '', component: Input }); // → FieldNode<string>
|
|
8248
|
+
```
|
|
8249
|
+
|
|
8250
|
+
_Source: src/form/factories/node-factory.ts_
|
|
8251
|
+
|
|
8252
|
+
### nonNegative
|
|
8253
|
+
|
|
8254
|
+
**Kind:** `function`
|
|
8255
|
+
|
|
8256
|
+
Фабрика валидатора, проверяющего что число неотрицательное (`≥ 0`).
|
|
8257
|
+
|
|
8258
|
+
Пустые значения и не-числа пропускаются (используйте {@link required} и {@link isNumber}).
|
|
8259
|
+
|
|
8260
|
+
**Signature:**
|
|
8261
|
+
```typescript
|
|
8262
|
+
export function nonNegative<TForm = unknown, TField extends number | null | undefined = number>(
|
|
8263
|
+
options?: ValidateOptions
|
|
8264
|
+
): Validator<TForm, TField>
|
|
8265
|
+
```
|
|
8266
|
+
|
|
8267
|
+
**Parameters:**
|
|
8268
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
8269
|
+
|
|
8270
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
8271
|
+
|
|
8272
|
+
**Examples:**
|
|
8273
|
+
|
|
8274
|
+
Проверка неотрицательности
|
|
8275
|
+
```typescript
|
|
8276
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8277
|
+
import { nonNegative } from '@reformer/core/validators';
|
|
8278
|
+
|
|
8279
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8280
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8281
|
+
validate(model.$.balance, [nonNegative({ message: 'Баланс не может быть отрицательным' })]);
|
|
8282
|
+
});
|
|
8283
|
+
```
|
|
8284
|
+
|
|
8285
|
+
_Source: src/form/validators/non-negative.ts_
|
|
8286
|
+
|
|
8287
|
+
### nonZero
|
|
8288
|
+
|
|
8289
|
+
**Kind:** `function`
|
|
8290
|
+
|
|
8291
|
+
Фабрика валидатора, проверяющего что число не равно нулю.
|
|
8292
|
+
|
|
8293
|
+
Пустые значения и не-числа пропускаются (используйте {@link required} и {@link isNumber}).
|
|
8294
|
+
|
|
8295
|
+
**Signature:**
|
|
8296
|
+
```typescript
|
|
8297
|
+
export function nonZero<TForm = unknown, TField extends number | null | undefined = number>(
|
|
8298
|
+
options?: ValidateOptions
|
|
8299
|
+
): Validator<TForm, TField>
|
|
8300
|
+
```
|
|
8301
|
+
|
|
8302
|
+
**Parameters:**
|
|
8303
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
8304
|
+
|
|
8305
|
+
**Returns:** Чистый валидатор {@link Validator} для числового поля
|
|
8306
|
+
|
|
8307
|
+
**Examples:**
|
|
8308
|
+
|
|
8309
|
+
Проверка «не ноль»
|
|
8310
|
+
```typescript
|
|
8311
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8312
|
+
import { nonZero } from '@reformer/core/validators';
|
|
8313
|
+
|
|
8314
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8315
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8316
|
+
validate(model.$.divisor, [nonZero({ message: 'Не может быть нулём' })]);
|
|
8317
|
+
});
|
|
8318
|
+
```
|
|
8319
|
+
|
|
8320
|
+
_Source: src/form/validators/non-zero.ts_
|
|
8321
|
+
|
|
8322
|
+
### onChange
|
|
8323
|
+
|
|
8324
|
+
**Kind:** `function`
|
|
8325
|
+
|
|
8326
|
+
Реакция на изменение поля; `{ debounce, immediate }`.
|
|
8327
|
+
|
|
8328
|
+
Колбэк выполняется ВНЕ effect-контекста (микротаск/таймер) — можно безопасно писать сигналы и ноды
|
|
8329
|
+
(`updateComponentProps`/`reset`/`clear`) без ручного `defer` и без «Cycle detected».
|
|
8330
|
+
|
|
8331
|
+
Для async-колбэков 2-м аргументом приходит `{ signal }` (AbortSignal): при следующей смене значения
|
|
8332
|
+
предыдущий `signal` аннулируется. Передавай его в `fetch` (сетевая отмена) или проверяй
|
|
8333
|
+
`signal.aborted` перед применением результата — это убирает гонки устаревших ответов (F2).
|
|
8334
|
+
|
|
8335
|
+
**Signature:**
|
|
8336
|
+
```typescript
|
|
8337
|
+
export function onChange<T>(
|
|
8338
|
+
source: ReadonlySignal<T>,
|
|
8339
|
+
cb: (value: T, ctx: ChangeContext) => void,
|
|
8340
|
+
options?: { immediate?: boolean; debounce?: number }
|
|
8341
|
+
): void
|
|
8342
|
+
```
|
|
8343
|
+
|
|
8344
|
+
_Source: src/form/behaviors/operators.ts_
|
|
8345
|
+
|
|
8346
|
+
### onDispose
|
|
8347
|
+
|
|
8348
|
+
**Kind:** `function`
|
|
8349
|
+
|
|
8350
|
+
Зарегистрировать произвольную отписку в активной схеме.
|
|
8351
|
+
|
|
8352
|
+
**Signature:**
|
|
8353
|
+
```typescript
|
|
8354
|
+
export function onDispose(cleanup: BehaviorCleanup): void
|
|
8355
|
+
```
|
|
8356
|
+
|
|
8357
|
+
_Source: src/form/behaviors/context.ts_
|
|
8358
|
+
|
|
8359
|
+
### pastDate
|
|
8360
|
+
|
|
8361
|
+
**Kind:** `function`
|
|
8362
|
+
|
|
8363
|
+
Фабрика валидатора, проверяющего что дата не в будущем.
|
|
8364
|
+
|
|
8365
|
+
Дата не должна быть позже сегодняшнего дня (сравнение по нормализованным датам).
|
|
8366
|
+
Пустые и невалидные даты пропускаются (используйте {@link required} и {@link isDate}).
|
|
8367
|
+
|
|
8368
|
+
**Signature:**
|
|
8369
|
+
```typescript
|
|
8370
|
+
export function pastDate<
|
|
8371
|
+
TForm = unknown,
|
|
8372
|
+
TField extends string | Date | null | undefined = string | Date,
|
|
8373
|
+
>(options?: ValidateOptions): Validator<TForm, TField>
|
|
8374
|
+
```
|
|
8375
|
+
|
|
8376
|
+
**Parameters:**
|
|
8377
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
8378
|
+
|
|
8379
|
+
**Returns:** Чистый валидатор {@link Validator} для поля даты (`string | Date`)
|
|
8380
|
+
|
|
8381
|
+
**Examples:**
|
|
8382
|
+
|
|
8383
|
+
Дата не в будущем
|
|
8384
|
+
```typescript
|
|
8385
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8386
|
+
import { required, pastDate } from '@reformer/core/validators';
|
|
6744
8387
|
|
|
6745
|
-
|
|
6746
|
-
const
|
|
8388
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8389
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8390
|
+
validate(model.$.birthDate, [required(), pastDate({ message: 'Дата рождения не может быть в будущем' })]);
|
|
8391
|
+
});
|
|
6747
8392
|
```
|
|
6748
8393
|
|
|
6749
|
-
_Source: src/form/
|
|
8394
|
+
_Source: src/form/validators/past-date.ts_
|
|
6750
8395
|
|
|
6751
8396
|
### PathAwareSignal
|
|
6752
8397
|
|
|
@@ -6765,6 +8410,118 @@ export type PathAwareSignal<T> = Signal<T> & {
|
|
|
6765
8410
|
|
|
6766
8411
|
_Source: src/model/types.ts_
|
|
6767
8412
|
|
|
8413
|
+
### pattern
|
|
8414
|
+
|
|
8415
|
+
**Kind:** `function`
|
|
8416
|
+
|
|
8417
|
+
Фабрика валидатора регулярного выражения.
|
|
8418
|
+
|
|
8419
|
+
Пустые значения (`''`/`null`/`undefined`) пропускаются (используйте {@link required}
|
|
8420
|
+
для обязательности).
|
|
8421
|
+
|
|
8422
|
+
**Signature:**
|
|
8423
|
+
```typescript
|
|
8424
|
+
export function pattern<TForm = unknown, TField extends string | null | undefined = string>(
|
|
8425
|
+
regex: RegExp,
|
|
8426
|
+
options?: ValidateOptions
|
|
8427
|
+
): Validator<TForm, TField>
|
|
8428
|
+
```
|
|
8429
|
+
|
|
8430
|
+
**Parameters:**
|
|
8431
|
+
- `regex` — - Регулярное выражение для проверки значения
|
|
8432
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}). В `params` ошибки автоматически
|
|
8433
|
+
попадает `pattern` (строка-источник regex).
|
|
8434
|
+
|
|
8435
|
+
**Returns:** Чистый валидатор {@link Validator} для строкового поля
|
|
8436
|
+
|
|
8437
|
+
**Examples:**
|
|
8438
|
+
|
|
8439
|
+
Проверка по регулярному выражению
|
|
8440
|
+
```typescript
|
|
8441
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8442
|
+
import { required, pattern } from '@reformer/core/validators';
|
|
8443
|
+
|
|
8444
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8445
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8446
|
+
validate(model.$.name, [pattern(/^[a-zA-Zа-яА-Я]+$/, { message: 'Только буквы' })]);
|
|
8447
|
+
validate(model.$.phone, [required(), pattern(/^\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}$/, { message: 'Формат +7 (999) 123-45-67' })]);
|
|
8448
|
+
});
|
|
8449
|
+
```
|
|
8450
|
+
|
|
8451
|
+
_Source: src/form/validators/pattern.ts_
|
|
8452
|
+
|
|
8453
|
+
### phone
|
|
8454
|
+
|
|
8455
|
+
**Kind:** `function`
|
|
8456
|
+
|
|
8457
|
+
Фабрика валидатора номера телефона.
|
|
8458
|
+
|
|
8459
|
+
Проверяет значение по regex выбранного {@link PhoneFormat}. Пустые значения
|
|
8460
|
+
(`''`/`null`/`undefined`) пропускаются (используйте {@link required} для обязательности).
|
|
8461
|
+
|
|
8462
|
+
**Signature:**
|
|
8463
|
+
```typescript
|
|
8464
|
+
export function phone<TForm = unknown, TField extends string | null | undefined = string>(
|
|
8465
|
+
options?: PhoneValidatorOptions
|
|
8466
|
+
): Validator<TForm, TField>
|
|
8467
|
+
```
|
|
8468
|
+
|
|
8469
|
+
**Parameters:**
|
|
8470
|
+
- `options` — - Опции валидатора {@link PhoneValidatorOptions}. В `params` ошибки
|
|
8471
|
+
автоматически попадает выбранный `format`.
|
|
8472
|
+
|
|
8473
|
+
**Returns:** Чистый валидатор {@link Validator} для строкового поля
|
|
8474
|
+
|
|
8475
|
+
**Examples:**
|
|
8476
|
+
|
|
8477
|
+
Проверка номера телефона
|
|
8478
|
+
```typescript
|
|
8479
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8480
|
+
import { required, phone } from '@reformer/core/validators';
|
|
8481
|
+
|
|
8482
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8483
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8484
|
+
validate(model.$.phone, [required(), phone({ format: 'ru' })]);
|
|
8485
|
+
});
|
|
8486
|
+
```
|
|
8487
|
+
|
|
8488
|
+
_Source: src/form/validators/phone.ts_
|
|
8489
|
+
|
|
8490
|
+
### PhoneFormat
|
|
8491
|
+
|
|
8492
|
+
**Kind:** `type`
|
|
8493
|
+
|
|
8494
|
+
Формат проверки номера телефона для валидатора {@link phone}.
|
|
8495
|
+
|
|
8496
|
+
- `international` — международный формат E.164 (`+?[1-9]\d{1,14}`);
|
|
8497
|
+
- `ru` — российские номера (`+7`/`7`/`8`, коды `4`/`8`/`9`, с разделителями);
|
|
8498
|
+
- `us` — североамериканские номера (NANP);
|
|
8499
|
+
- `any` — свободный формат: цифры, скобки и разделители (по умолчанию).
|
|
8500
|
+
|
|
8501
|
+
**Signature:**
|
|
8502
|
+
```typescript
|
|
8503
|
+
export type PhoneFormat = 'international' | 'ru' | 'us' | 'any';
|
|
8504
|
+
```
|
|
8505
|
+
|
|
8506
|
+
_Source: src/form/validators/phone.ts_
|
|
8507
|
+
|
|
8508
|
+
### PhoneValidatorOptions
|
|
8509
|
+
|
|
8510
|
+
**Kind:** `interface`
|
|
8511
|
+
|
|
8512
|
+
Опции валидатора {@link phone}. Расширяют {@link ValidateOptions} (`message`, `params`)
|
|
8513
|
+
выбором формата номера.
|
|
8514
|
+
|
|
8515
|
+
**Signature:**
|
|
8516
|
+
```typescript
|
|
8517
|
+
export interface PhoneValidatorOptions extends ValidateOptions {
|
|
8518
|
+
/** Формат проверки {@link PhoneFormat}. По умолчанию `'any'`. */
|
|
8519
|
+
format?: PhoneFormat;
|
|
8520
|
+
}
|
|
8521
|
+
```
|
|
8522
|
+
|
|
8523
|
+
_Source: src/form/validators/phone.ts_
|
|
8524
|
+
|
|
6768
8525
|
### registerSignalNode
|
|
6769
8526
|
|
|
6770
8527
|
**Kind:** `function`
|
|
@@ -6799,6 +8556,47 @@ if (sig && node) registerSignalNode(sig, node);
|
|
|
6799
8556
|
|
|
6800
8557
|
_Source: src/form/signal-node-registry.ts_
|
|
6801
8558
|
|
|
8559
|
+
### required
|
|
8560
|
+
|
|
8561
|
+
**Kind:** `function`
|
|
8562
|
+
|
|
8563
|
+
Фабрика валидатора обязательного поля.
|
|
8564
|
+
|
|
8565
|
+
Возвращает чистую функцию-валидатор `(value, control, root)`. Передаётся в `validate()`.
|
|
8566
|
+
|
|
8567
|
+
Пустыми считаются: `null`, `undefined`, `''` (пустая строка), `[]` (пустой массив —
|
|
8568
|
+
обязательный multi-select / FormArray без выбранных элементов).
|
|
8569
|
+
Для boolean полей требуется значение `true`.
|
|
8570
|
+
|
|
8571
|
+
**Signature:**
|
|
8572
|
+
```typescript
|
|
8573
|
+
export function required<TForm = unknown, TField = unknown>(
|
|
8574
|
+
options?: ValidateOptions
|
|
8575
|
+
): Validator<TForm, TField>
|
|
8576
|
+
```
|
|
8577
|
+
|
|
8578
|
+
**Parameters:**
|
|
8579
|
+
- `options` — - Опции валидатора ({@link ValidateOptions}): `message`, `params`
|
|
8580
|
+
|
|
8581
|
+
**Returns:** Чистый валидатор {@link Validator} для поля схемы
|
|
8582
|
+
|
|
8583
|
+
**Examples:**
|
|
8584
|
+
|
|
8585
|
+
Обязательные поля в схеме формы
|
|
8586
|
+
```typescript
|
|
8587
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
8588
|
+
import { required } from '@reformer/core/validators';
|
|
8589
|
+
|
|
8590
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
8591
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
8592
|
+
validate(model.$.email, [required()]);
|
|
8593
|
+
validate(model.$.phone, [required({ message: 'Укажите номер телефона' })]);
|
|
8594
|
+
validate(model.$.agreeToTerms, [required({ message: 'Необходимо принять условия' })]);
|
|
8595
|
+
});
|
|
8596
|
+
```
|
|
8597
|
+
|
|
8598
|
+
_Source: src/form/validators/required.ts_
|
|
8599
|
+
|
|
6802
8600
|
### resetWhen
|
|
6803
8601
|
|
|
6804
8602
|
**Kind:** `function`
|
|
@@ -6845,6 +8643,25 @@ revalidateWhen([model.$.maxAmount], () => void validateModel(model, schema));
|
|
|
6845
8643
|
|
|
6846
8644
|
_Source: src/model/behaviors-value.ts_
|
|
6847
8645
|
|
|
8646
|
+
### Rule
|
|
8647
|
+
|
|
8648
|
+
**Kind:** `type`
|
|
8649
|
+
|
|
8650
|
+
Синхронное правило поля типа `TField`. Проверяется ТОЛЬКО значение (`value`).
|
|
8651
|
+
|
|
8652
|
+
Позиционные `scope`/`root` помечены `never` намеренно: так встроенные value-only фабрики
|
|
8653
|
+
(`required()`/`min()`/… — они `(value, model, root) => …`) и inline-правила `(value) => …`
|
|
8654
|
+
ОБА присваиваются в `Rule<TField>[]` без `any`, при этом сохраняется проверка типа поля
|
|
8655
|
+
(`validate(model.$.age, [email()])` подсветится — `email` ждёт `string`, поле `number`).
|
|
8656
|
+
На вызове раннер приводит правило к callable и передаёт `(value, model, model)`.
|
|
8657
|
+
|
|
8658
|
+
**Signature:**
|
|
8659
|
+
```typescript
|
|
8660
|
+
export type Rule<TField> = (value: TField, scope: never, root: never) => ValidationError | null;
|
|
8661
|
+
```
|
|
8662
|
+
|
|
8663
|
+
_Source: src/form/validation/types.ts_
|
|
8664
|
+
|
|
6848
8665
|
### runOutsideEffect
|
|
6849
8666
|
|
|
6850
8667
|
**Kind:** `function`
|
|
@@ -7123,6 +8940,27 @@ syncFields(model.$.field1, model.$.field2);
|
|
|
7123
8940
|
|
|
7124
8941
|
_Source: src/model/behaviors-value.ts_
|
|
7125
8942
|
|
|
8943
|
+
### toFileArray
|
|
8944
|
+
|
|
8945
|
+
**Kind:** `function`
|
|
8946
|
+
|
|
8947
|
+
Нормализует значение file-поля в массив файлоподобных элементов.
|
|
8948
|
+
|
|
8949
|
+
Одиночный {@link FileLike} оборачивается в массив; из массива отбираются только
|
|
8950
|
+
файлоподобные элементы. Для прочих значений — `null` (валидатор пропускает).
|
|
8951
|
+
|
|
8952
|
+
**Signature:**
|
|
8953
|
+
```typescript
|
|
8954
|
+
export function toFileArray(value: unknown): FileLike[] | null
|
|
8955
|
+
```
|
|
8956
|
+
|
|
8957
|
+
**Parameters:**
|
|
8958
|
+
- `value` — - Значение поля (`FileLike`, `FileLike[]` или что-то ещё)
|
|
8959
|
+
|
|
8960
|
+
**Returns:** Массив файлоподобных элементов или `null`
|
|
8961
|
+
|
|
8962
|
+
_Source: src/form/validators/file-utils.ts_
|
|
8963
|
+
|
|
7126
8964
|
### transformValue
|
|
7127
8965
|
|
|
7128
8966
|
**Kind:** `function`
|
|
@@ -7196,6 +9034,67 @@ onDispose(() => unmarkDerived(model.$.total)); // при снятии compute с
|
|
|
7196
9034
|
|
|
7197
9035
|
_Source: src/model/derived-registry.ts_
|
|
7198
9036
|
|
|
9037
|
+
### url
|
|
9038
|
+
|
|
9039
|
+
**Kind:** `function`
|
|
9040
|
+
|
|
9041
|
+
Фабрика валидатора URL.
|
|
9042
|
+
|
|
9043
|
+
Пустые значения (`''`/`null`/`undefined`) пропускаются (используйте {@link required}
|
|
9044
|
+
для обязательности). При `requireProtocol` протокол обязателен; `allowedProtocols`
|
|
9045
|
+
дополнительно ограничивает набор допустимых протоколов.
|
|
9046
|
+
|
|
9047
|
+
**Signature:**
|
|
9048
|
+
```typescript
|
|
9049
|
+
export function url<TForm = unknown, TField extends string | null | undefined = string>(
|
|
9050
|
+
options?: UrlValidatorOptions
|
|
9051
|
+
): Validator<TForm, TField>
|
|
9052
|
+
```
|
|
9053
|
+
|
|
9054
|
+
**Parameters:**
|
|
9055
|
+
- `options` — - Опции валидатора {@link UrlValidatorOptions}
|
|
9056
|
+
|
|
9057
|
+
**Returns:** Чистый валидатор {@link Validator} для строкового поля
|
|
9058
|
+
|
|
9059
|
+
**Examples:**
|
|
9060
|
+
|
|
9061
|
+
Проверка URL
|
|
9062
|
+
```typescript
|
|
9063
|
+
import { defineValidationSchema, validate } from '@reformer/core/validation';
|
|
9064
|
+
import { required, url } from '@reformer/core/validators';
|
|
9065
|
+
|
|
9066
|
+
// Правила живут в отдельной схеме над МОДЕЛЬЮ — layout-схема валидаторов не несёт.
|
|
9067
|
+
const validation = defineValidationSchema<MyForm>(({ model }) => {
|
|
9068
|
+
validate(model.$.website, [required(), url({ message: 'Введите корректный URL' })]);
|
|
9069
|
+
// Требовать протокол и ограничить схему только https:
|
|
9070
|
+
validate(model.$.homepage, [url({ requireProtocol: true, allowedProtocols: ['https'] })]);
|
|
9071
|
+
});
|
|
9072
|
+
```
|
|
9073
|
+
|
|
9074
|
+
_Source: src/form/validators/url.ts_
|
|
9075
|
+
|
|
9076
|
+
### UrlValidatorOptions
|
|
9077
|
+
|
|
9078
|
+
**Kind:** `interface`
|
|
9079
|
+
|
|
9080
|
+
Опции валидатора {@link url}. Расширяют {@link ValidateOptions} (`message`, `params`)
|
|
9081
|
+
настройками проверки протокола.
|
|
9082
|
+
|
|
9083
|
+
**Signature:**
|
|
9084
|
+
```typescript
|
|
9085
|
+
export interface UrlValidatorOptions extends ValidateOptions {
|
|
9086
|
+
/** Требовать наличие протокола (`http://` или `https://`). По умолчанию `false`. */
|
|
9087
|
+
requireProtocol?: boolean;
|
|
9088
|
+
/**
|
|
9089
|
+
* Список разрешённых протоколов (без `://`), например `['https']`. Если задан,
|
|
9090
|
+
* значение с иным протоколом даёт ошибку с кодом `url_protocol`.
|
|
9091
|
+
*/
|
|
9092
|
+
allowedProtocols?: string[];
|
|
9093
|
+
}
|
|
9094
|
+
```
|
|
9095
|
+
|
|
9096
|
+
_Source: src/form/validators/url.ts_
|
|
9097
|
+
|
|
7199
9098
|
### useArrayLength
|
|
7200
9099
|
|
|
7201
9100
|
**Kind:** `function`
|
|
@@ -7234,6 +9133,33 @@ function ArrayRenderer({ arrayNode }) {
|
|
|
7234
9133
|
|
|
7235
9134
|
_Source: src/platforms/react/hooks/useArrayLength.ts_
|
|
7236
9135
|
|
|
9136
|
+
### useFieldHandleRef
|
|
9137
|
+
|
|
9138
|
+
**Kind:** `function`
|
|
9139
|
+
|
|
9140
|
+
Ref для контрола поля + императивный handle для потребителя.
|
|
9141
|
+
|
|
9142
|
+
Возвращённый ref вешается на контрол. На `outerRef` публикуется:
|
|
9143
|
+
- handle самого контрола, если тот его реализует (`useImperativeHandle` композита);
|
|
9144
|
+
- иначе — базовый {@link FieldHandle}, построенный из DOM-узла контрола.
|
|
9145
|
+
|
|
9146
|
+
Хук вызывается безусловно; вешать ref на контрол стоит только при заданном `outerRef` —
|
|
9147
|
+
так function-компонент без ref-поддержки не получает лишний ref.
|
|
9148
|
+
|
|
9149
|
+
**Signature:**
|
|
9150
|
+
```typescript
|
|
9151
|
+
export function useFieldHandleRef<H = FieldHandle>(
|
|
9152
|
+
outerRef: Ref<H> | undefined
|
|
9153
|
+
): RefObject<unknown>
|
|
9154
|
+
```
|
|
9155
|
+
|
|
9156
|
+
**Parameters:**
|
|
9157
|
+
- `outerRef` — - ref потребителя (из `forwardRef` или `schema.node(sel).getRef()`).
|
|
9158
|
+
|
|
9159
|
+
**Returns:** ref, который нужно передать контролу.
|
|
9160
|
+
|
|
9161
|
+
_Source: src/platforms/react/field-binding/field-handle.ts_
|
|
9162
|
+
|
|
7237
9163
|
### useFormBundle
|
|
7238
9164
|
|
|
7239
9165
|
**Kind:** `function`
|
|
@@ -7749,6 +9675,96 @@ export interface UseFormValidationResult {
|
|
|
7749
9675
|
|
|
7750
9676
|
_Source: src/platforms/react/hooks/use-form-validation.ts_
|
|
7751
9677
|
|
|
9678
|
+
### validate
|
|
9679
|
+
|
|
9680
|
+
**Kind:** `function`
|
|
9681
|
+
|
|
9682
|
+
Синхронные правила поля.
|
|
9683
|
+
|
|
9684
|
+
**Signature:**
|
|
9685
|
+
```typescript
|
|
9686
|
+
export function validate<TField>(sig: PathAwareSignal<TField>, rules: Rule<TField>[]): void
|
|
9687
|
+
```
|
|
9688
|
+
|
|
9689
|
+
**Examples:**
|
|
9690
|
+
|
|
9691
|
+
```ts
|
|
9692
|
+
validate(model.$.loanAmount, [required({ message: 'Сумма' }), min(50000)]);
|
|
9693
|
+
```
|
|
9694
|
+
|
|
9695
|
+
_Source: src/form/validation/operators.ts_
|
|
9696
|
+
|
|
9697
|
+
### validateAsync
|
|
9698
|
+
|
|
9699
|
+
**Kind:** `function`
|
|
9700
|
+
|
|
9701
|
+
Асинхронные правила поля (зеркалит движковое разделение `validators` / `asyncValidators`).
|
|
9702
|
+
Раннер дожидается их и прокидывает `AbortSignal` для отмены устаревших ответов.
|
|
9703
|
+
|
|
9704
|
+
**Signature:**
|
|
9705
|
+
```typescript
|
|
9706
|
+
export function validateAsync<TField>(
|
|
9707
|
+
sig: PathAwareSignal<TField>,
|
|
9708
|
+
rules: AsyncRule<TField>[]
|
|
9709
|
+
): void
|
|
9710
|
+
```
|
|
9711
|
+
|
|
9712
|
+
**Examples:**
|
|
9713
|
+
|
|
9714
|
+
```ts
|
|
9715
|
+
validateAsync(model.$.username, [async (v, { signal }) => {
|
|
9716
|
+
const r = await fetch(`/api/free?u=${v}`, { signal });
|
|
9717
|
+
return (await r.json()).free ? null : { code: 'taken', message: 'Занято' };
|
|
9718
|
+
}]);
|
|
9719
|
+
```
|
|
9720
|
+
|
|
9721
|
+
_Source: src/form/validation/operators.ts_
|
|
9722
|
+
|
|
9723
|
+
### validateModel
|
|
9724
|
+
|
|
9725
|
+
**Kind:** `function`
|
|
9726
|
+
|
|
9727
|
+
Провалидировать модель ЛЮБОЙ схемой (шаг или вся форма). Открывает ambient-окно на время
|
|
9728
|
+
синхронного прогона `schema`, дожидается async-правил, разносит ошибки по нодам формы и гасит
|
|
9729
|
+
поля, ставшие валидными. Возвращает `true`, если нет блокирующих ошибок (`severity:'warning'` не блокирует).
|
|
9730
|
+
|
|
9731
|
+
Гашение — на пару (model, schema): `validateModel(model, step2)` и `validateModel(model, form)` не мешают
|
|
9732
|
+
друг другу. Устаревший прогон (быстрый повторный вызов той же (model, schema)) отменяется через `AbortSignal`
|
|
9733
|
+
и возвращает `false` (**fail-closed** — отменённому результату нельзя доверять для submit).
|
|
9734
|
+
|
|
9735
|
+
⚠️ `schema` должна быть СТАБИЛЬНОЙ ссылкой: отмена устаревших прогонов ключится по идентичности `schema`.
|
|
9736
|
+
Инлайн-стрелка (`validateModel(model, ({ model }) => …)`) каждый раз создаёт НОВЫЙ прогон без дедупликации —
|
|
9737
|
+
держите схемы в `const` / `defineValidationSchema`.
|
|
9738
|
+
|
|
9739
|
+
`options.touch` (§6): по завершении помечает `touched` ИМЕННО поля, которые схема проверяла
|
|
9740
|
+
(не всё поддерево). Ошибки становятся видимыми (`shouldShowError = invalid && (touched||dirty)`)
|
|
9741
|
+
без ручного `form.markAsTouched()`; при пошаговой валидации следующий шаг не показывается
|
|
9742
|
+
«тронутым» до ввода. Валидные проверенные поля тоже метятся — это безвредно (ошибка не покажется).
|
|
9743
|
+
|
|
9744
|
+
**Signature:**
|
|
9745
|
+
```typescript
|
|
9746
|
+
export async function validateModel<T>(
|
|
9747
|
+
model: FormModel<T>,
|
|
9748
|
+
schema: ValidationSchema<T>,
|
|
9749
|
+
options?: { touch?: boolean }
|
|
9750
|
+
): Promise<boolean>
|
|
9751
|
+
```
|
|
9752
|
+
|
|
9753
|
+
**Parameters:**
|
|
9754
|
+
- `model` — - Модель данных формы.
|
|
9755
|
+
- `schema` — - Схема валидации (стабильная ссылка).
|
|
9756
|
+
- `options` — - `{ touch }` — пометить провалидированные поля `touched` для показа ошибок.
|
|
9757
|
+
|
|
9758
|
+
**Examples:**
|
|
9759
|
+
|
|
9760
|
+
```ts
|
|
9761
|
+
const ok = await validateModel(model, step2Validation); // один шаг
|
|
9762
|
+
const all = await validateModel(model, formValidation); // вся форма
|
|
9763
|
+
const stepOk = await validateModel(model, step2Validation, { touch: true }); // + показать ошибки шага
|
|
9764
|
+
```
|
|
9765
|
+
|
|
9766
|
+
_Source: src/form/validation/run.ts_
|
|
9767
|
+
|
|
7752
9768
|
### ValidateOptions
|
|
7753
9769
|
|
|
7754
9770
|
**Kind:** `interface`
|
|
@@ -7769,6 +9785,20 @@ export interface ValidateOptions {
|
|
|
7769
9785
|
|
|
7770
9786
|
_Source: src/form/types/validation-schema.ts_
|
|
7771
9787
|
|
|
9788
|
+
### validateWhen
|
|
9789
|
+
|
|
9790
|
+
**Kind:** `function`
|
|
9791
|
+
|
|
9792
|
+
Условная валидация: правила внутри `cb` активны, только пока `cond()` истинно; иначе их поля
|
|
9793
|
+
ГАСЯТСЯ (пустой bucket → `setErrors([])`). Не трогает включение/сброс поля — это дело поведения (`enableWhen`).
|
|
9794
|
+
|
|
9795
|
+
**Signature:**
|
|
9796
|
+
```typescript
|
|
9797
|
+
export function validateWhen(cond: () => boolean, cb: () => void): void
|
|
9798
|
+
```
|
|
9799
|
+
|
|
9800
|
+
_Source: src/form/validation/operators.ts_
|
|
9801
|
+
|
|
7772
9802
|
### ValidationError
|
|
7773
9803
|
|
|
7774
9804
|
**Kind:** `interface`
|
|
@@ -7788,6 +9818,54 @@ export interface ValidationError {
|
|
|
7788
9818
|
|
|
7789
9819
|
_Source: src/form/types/contracts.ts_
|
|
7790
9820
|
|
|
9821
|
+
### ValidationSchema
|
|
9822
|
+
|
|
9823
|
+
**Kind:** `type`
|
|
9824
|
+
|
|
9825
|
+
Схема валидации — обычная функция над (под)моделью. First-class значение (можно `apply`/тестировать/переиспользовать).
|
|
9826
|
+
|
|
9827
|
+
**Signature:**
|
|
9828
|
+
```typescript
|
|
9829
|
+
export type ValidationSchema<T> = (ctx: { model: FormModel<T> }) => void;
|
|
9830
|
+
```
|
|
9831
|
+
|
|
9832
|
+
_Source: src/form/validation/types.ts_
|
|
9833
|
+
|
|
9834
|
+
### ValidationStrategyKind
|
|
9835
|
+
|
|
9836
|
+
**Kind:** `type`
|
|
9837
|
+
|
|
9838
|
+
Когда запускается schema-валидация:
|
|
9839
|
+
- `submit` (по умолчанию) — только явным вызовом `validate()` (на отправке);
|
|
9840
|
+
- `blur` — при потере фокуса поля (смена `touched` любого листа);
|
|
9841
|
+
- `change` — на каждый ввод (с опц. `debounce`);
|
|
9842
|
+
- `afterFirstSubmit` — тихо до первого `validate()`, затем live (см. `liveAfterSubmit`).
|
|
9843
|
+
|
|
9844
|
+
**Signature:**
|
|
9845
|
+
```typescript
|
|
9846
|
+
export type ValidationStrategyKind = 'submit' | 'blur' | 'change' | 'afterFirstSubmit';
|
|
9847
|
+
```
|
|
9848
|
+
|
|
9849
|
+
_Source: src/form/validation/strategy.ts_
|
|
9850
|
+
|
|
9851
|
+
### ValidationStrategyOptions
|
|
9852
|
+
|
|
9853
|
+
**Kind:** `interface`
|
|
9854
|
+
|
|
9855
|
+
**Signature:**
|
|
9856
|
+
```typescript
|
|
9857
|
+
export interface ValidationStrategyOptions {
|
|
9858
|
+
/** Стратегия запуска. Default `'submit'`. */
|
|
9859
|
+
strategy?: ValidationStrategyKind;
|
|
9860
|
+
/** Debounce (мс) для live-фаз (`change` и live-часть `afterFirstSubmit`). Default `0`. */
|
|
9861
|
+
debounce?: number;
|
|
9862
|
+
/** Режим live-фазы для `afterFirstSubmit`. Default `'change'`. */
|
|
9863
|
+
liveAfterSubmit?: 'change' | 'blur';
|
|
9864
|
+
}
|
|
9865
|
+
```
|
|
9866
|
+
|
|
9867
|
+
_Source: src/form/validation/strategy.ts_
|
|
9868
|
+
|
|
7791
9869
|
### Validator
|
|
7792
9870
|
|
|
7793
9871
|
**Kind:** `type`
|