@reformer/core 11.0.0-beta.8 → 11.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -73
- package/dist/behaviors-value-HA_Q03hc.js +108 -0
- package/dist/behaviors.js +370 -242
- package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +28 -7
- package/dist/form/behaviors/context.d.ts +19 -2
- package/dist/form/behaviors/index.d.ts +3 -3
- package/dist/form/behaviors/internals.d.ts +9 -2
- package/dist/form/behaviors/node.d.ts +25 -2
- package/dist/form/behaviors/operators.d.ts +17 -9
- package/dist/form/behaviors/schema-operators.d.ts +6 -4
- package/dist/form/behaviors/types.d.ts +12 -4
- package/dist/form/create-form.d.ts +47 -35
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +45 -28
- package/dist/form/form-config-base.d.ts +46 -0
- package/dist/form/form-level.d.ts +121 -0
- package/dist/form/form-proxy-builder.d.ts +6 -22
- package/dist/form/index.d.ts +16 -21
- package/dist/form/nodes/form-array-node.d.ts +100 -0
- package/dist/form/nodes/form-control-node.d.ts +73 -0
- package/dist/form/nodes/form-group-node.d.ts +66 -0
- package/dist/form/nodes/node-kind.d.ts +29 -0
- package/dist/form/nodes/state/availability.d.ts +6 -0
- package/dist/form/nodes/state/component.d.ts +6 -0
- package/dist/form/nodes/state/index.d.ts +12 -0
- package/dist/form/nodes/state/interaction.d.ts +13 -0
- package/dist/form/nodes/state/issues.d.ts +9 -0
- package/dist/form/nodes/state/pending.d.ts +6 -0
- package/dist/form/schema/compile.d.ts +59 -0
- package/dist/form/schema/compiled.d.ts +143 -0
- package/dist/form/schema/row-key.d.ts +11 -0
- package/dist/form/schema-controller.d.ts +30 -14
- package/dist/form/signal-node-registry.d.ts +6 -7
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +19 -92
- package/dist/form/types/field-config.d.ts +27 -0
- package/dist/form/types/index.d.ts +5 -57
- package/dist/form/types/nodes.d.ts +159 -0
- package/dist/form/types/schema-node.d.ts +139 -63
- package/dist/form/types/validation-schema.d.ts +23 -27
- package/dist/form/validation/context.d.ts +42 -7
- package/dist/form/validation/index.d.ts +9 -8
- package/dist/form/validation/operators.d.ts +22 -30
- package/dist/form/validation/run.d.ts +93 -18
- package/dist/form/validation/strategy.d.ts +41 -13
- package/dist/form/validation/types.d.ts +74 -20
- package/dist/form/validators/date-utils.d.ts +13 -1
- package/dist/form/validators/email.d.ts +8 -7
- package/dist/form/validators/file-type.d.ts +8 -7
- package/dist/form/validators/file-utils.d.ts +2 -1
- package/dist/form/validators/future-date.d.ts +8 -7
- package/dist/form/validators/index.d.ts +1 -1
- package/dist/form/validators/integer.d.ts +8 -7
- package/dist/form/validators/is-date.d.ts +11 -9
- package/dist/form/validators/is-number.d.ts +11 -9
- package/dist/form/validators/max-age.d.ts +13 -9
- package/dist/form/validators/max-date.d.ts +8 -7
- package/dist/form/validators/max-file-size.d.ts +8 -7
- package/dist/form/validators/max-files.d.ts +8 -7
- package/dist/form/validators/max-length.d.ts +14 -7
- package/dist/form/validators/max-total-file-size.d.ts +8 -7
- package/dist/form/validators/max.d.ts +10 -8
- package/dist/form/validators/min-age.d.ts +12 -9
- package/dist/form/validators/min-date.d.ts +8 -7
- package/dist/form/validators/min-file-size.d.ts +8 -7
- package/dist/form/validators/min-files.d.ts +11 -9
- package/dist/form/validators/min-length.d.ts +14 -7
- package/dist/form/validators/min.d.ts +10 -8
- package/dist/form/validators/multiple-of.d.ts +10 -8
- package/dist/form/validators/non-negative.d.ts +8 -7
- package/dist/form/validators/non-zero.d.ts +8 -7
- package/dist/form/validators/past-date.d.ts +8 -7
- package/dist/form/validators/pattern.d.ts +9 -8
- package/dist/form/validators/phone.d.ts +8 -7
- package/dist/form/validators/required.d.ts +11 -9
- package/dist/form/validators/url.d.ts +20 -13
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +1 -1
- package/dist/i18n/en.json.d.ts +5 -1
- package/dist/i18n/locale-store.d.ts +7 -0
- package/dist/i18n/message-format.d.ts +24 -4
- package/dist/i18n/ru.json.d.ts +5 -1
- package/dist/i18n/translator.d.ts +11 -0
- package/dist/i18n/validation-message.d.ts +42 -7
- package/dist/i18n.d.ts +7 -12
- package/dist/i18n.js +66 -283
- package/dist/index.d.ts +16 -7
- package/dist/index.js +924 -2855
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.js +1 -1
- package/dist/locale/en.json +4 -1
- package/dist/locale/ru.js +4 -1
- package/dist/locale/ru.json +4 -1
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.js +1 -1
- package/dist/model/behaviors-value.d.ts +24 -6
- package/dist/model/create-model.d.ts +2 -2
- package/dist/model/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +11 -5
- package/dist/model/model-nodes.d.ts +79 -12
- package/dist/model/model-signals-proxy.d.ts +5 -6
- package/dist/model/model-value-proxy.d.ts +20 -15
- package/dist/model/safe-effect.d.ts +33 -1
- package/dist/model/types.d.ts +63 -13
- package/dist/model.js +20 -18
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
- package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
- package/dist/platforms/react/form-bundle-context.d.ts +2 -2
- package/dist/platforms/react/hooks/types.d.ts +57 -38
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
- package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
- package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
- package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
- package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +6 -1
- package/dist/platforms/react/i18n/hooks.d.ts +16 -10
- package/dist/platforms/react/i18n/provider.d.ts +9 -2
- package/dist/platforms/react/index.d.ts +6 -4
- package/dist/react.d.ts +22 -0
- package/dist/react.js +505 -0
- package/dist/runtime-token.d.ts +1 -2
- package/dist/safe-effect-BbtX3KuN.js +643 -0
- package/dist/schema-controller-CpiO2JoK.js +143 -0
- package/dist/signals.d.ts +15 -9
- package/dist/signals.js +11 -6
- package/dist/strategy-CoFSemuP.js +375 -0
- package/dist/type-guards-D-N_5Ddh.js +19 -0
- package/dist/validation-issue-B9mjI8jz.js +7 -0
- package/dist/validation-message-Ced9MGxs.js +194 -0
- package/dist/validation.js +9 -9
- package/dist/validators/email.js +5 -5
- package/dist/validators/future-date.js +8 -8
- package/dist/validators/integer.js +3 -3
- package/dist/validators/is-date.js +4 -4
- package/dist/validators/is-number.js +4 -4
- package/dist/validators/max-age.js +8 -9
- package/dist/validators/max-date.js +8 -8
- package/dist/validators/max-length.js +7 -7
- package/dist/validators/max.js +4 -4
- package/dist/validators/min-age.js +10 -8
- package/dist/validators/min-date.js +3 -3
- package/dist/validators/min-length.js +5 -5
- package/dist/validators/min.js +4 -4
- package/dist/validators/multiple-of.js +11 -7
- package/dist/validators/non-negative.js +3 -3
- package/dist/validators/non-zero.js +3 -3
- package/dist/validators/past-date.js +8 -8
- package/dist/validators/pattern.js +5 -5
- package/dist/validators/phone.js +11 -9
- package/dist/validators/required.js +2 -2
- package/dist/validators/url.js +48 -12
- package/dist/validators.js +17 -17
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +111 -80
- package/docs/llms/03-api-signatures.md +368 -153
- package/docs/llms/04-common-patterns.md +58 -56
- package/docs/llms/05-common-mistakes.md +69 -44
- package/docs/llms/06-troubleshooting.md +14 -9
- package/docs/llms/07-complete-import.md +74 -43
- package/docs/llms/08-form-types.md +11 -4
- package/docs/llms/09-formschema.md +129 -81
- package/docs/llms/10-arrays.md +106 -75
- package/docs/llms/11-async-watchfield.md +2 -2
- package/docs/llms/13-multi-step.md +134 -63
- package/docs/llms/14-extended-mistakes.md +20 -17
- package/docs/llms/15-project-structure.md +221 -74
- package/docs/llms/16-ui-components.md +55 -42
- package/docs/llms/17-nonexistent-api.md +199 -32
- package/docs/llms/18-conditional-fields.md +66 -32
- package/docs/llms/19-reading-values.md +15 -3
- package/docs/llms/20-compute-vs-watch.md +16 -10
- package/docs/llms/21-array-operations.md +51 -27
- package/docs/llms/22-cycle-detection.md +1 -1
- package/docs/llms/23-copy-from.md +2 -2
- package/docs/llms/24-sync-fields.md +6 -5
- package/docs/llms/25-reset-when.md +14 -7
- package/docs/llms/26-transform-value.md +4 -4
- package/docs/llms/27-revalidate-when.md +16 -15
- package/docs/llms/28-submit-and-reset.md +276 -128
- package/docs/llms/29-async-preload.md +39 -29
- package/docs/llms/30-type-safety-recipes.md +105 -51
- package/docs/llms/31-async-validator-debounce.md +96 -53
- package/docs/llms/33-validation-strategy.md +177 -65
- package/docs/llms/34-i18n.md +69 -43
- package/docs/llms/35-unified-contract.md +100 -39
- package/docs/llms/36-issue-messages.md +284 -0
- package/docs/llms/37-compiled-schema.md +332 -0
- package/docs/llms/38-form-flow.md +397 -0
- package/llms-index.json +1 -1
- package/llms.txt +6084 -3420
- package/package.json +23 -4
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/form/create-core-form.d.ts +0 -72
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -189
- package/dist/form/form-submitter.d.ts +0 -121
- package/dist/form/nodes/array-node.d.ts +0 -276
- package/dist/form/nodes/field-node.d.ts +0 -297
- package/dist/form/nodes/form-node.d.ts +0 -326
- package/dist/form/nodes/group-node.d.ts +0 -284
- package/dist/form/nodes/model-array-node.d.ts +0 -126
- package/dist/form/nodes/subscription-manager.d.ts +0 -180
- package/dist/form/status-machine.d.ts +0 -115
- package/dist/form/types/deep-schema.d.ts +0 -107
- package/dist/form/types/form-proxy.d.ts +0 -141
- package/dist/form/unique-id.d.ts +0 -45
- package/dist/form/validation/config.d.ts +0 -73
- package/dist/form/validation/error-handler.d.ts +0 -166
- package/dist/form/validators/validation-error.d.ts +0 -8
- package/dist/index-BrNkQj1y.js +0 -105
- package/dist/loader-Dv_Hl7JC.js +0 -216
- package/dist/model-value-proxy-caTmAiUd.js +0 -419
- package/dist/node-BeV_lV5i.js +0 -100
- package/dist/strategy-DgzELWno.js +0 -204
- package/dist/validation-error-B-U3RsV0.js +0 -11
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Signal } from '@preact/signals-core';
|
|
2
2
|
import { PathAwareSignal } from './types';
|
|
3
|
-
import { ModelNode,
|
|
3
|
+
import { ModelNode, ModelGroupNode, ModelArrayNode } from './model-nodes';
|
|
4
4
|
/**
|
|
5
5
|
* Узел дерева `$`: лист → сам {@link PathAwareSignal}, группа/массив → контейнерный узел —
|
|
6
6
|
* {@link ReadonlySignal} агрегированного значения ПЛЮС доступ к детям по имени/индексу.
|
|
@@ -26,7 +26,7 @@ export declare function signalsProxy(node: ModelNode): any;
|
|
|
26
26
|
*
|
|
27
27
|
* @internal
|
|
28
28
|
*/
|
|
29
|
-
export declare function containerNodeOf(handle: unknown):
|
|
29
|
+
export declare function containerNodeOf(handle: unknown): ModelGroupNode | ModelArrayNode | undefined;
|
|
30
30
|
/**
|
|
31
31
|
* Узел дерева `model.$` — контейнер (группа/массив), а не лист?
|
|
32
32
|
*
|
|
@@ -44,12 +44,11 @@ export declare function isModelContainerSignal(value: unknown): boolean;
|
|
|
44
44
|
/** Узел-массив дерева `model.$` — ручка значения массива целиком. */
|
|
45
45
|
export declare function isModelArraySignal(value: unknown): boolean;
|
|
46
46
|
/**
|
|
47
|
-
* «Ручка значения», к которой можно привязать поле: лист (
|
|
47
|
+
* «Ручка значения», к которой можно привязать поле: лист (сигнал, не контейнер) либо узел-массив
|
|
48
48
|
* дерева `model.$` (значение — массив целиком, `.value` читается и пишется).
|
|
49
49
|
*
|
|
50
50
|
* Группа сюда не входит: она читается целиком, но не пишется и полем не бывает. Этой проверкой
|
|
51
|
-
* `createForm` и рендереры отличают узел-поле схемы (`{
|
|
52
|
-
* вместо голого `value instanceof Signal`, который узел-массив не проходит.
|
|
51
|
+
* `createForm` и рендереры отличают узел-поле схемы (`{ model, component }`) от контейнера.
|
|
53
52
|
*
|
|
54
53
|
* @group Model
|
|
55
54
|
* @example
|
|
@@ -61,4 +60,4 @@ export declare function isModelArraySignal(value: unknown): boolean;
|
|
|
61
60
|
* ```
|
|
62
61
|
*/
|
|
63
62
|
export declare function isValueSignal(value: unknown): value is Signal<unknown>;
|
|
64
|
-
export declare function resolveSignalAt(root:
|
|
63
|
+
export declare function resolveSignalAt(root: ModelGroupNode, path: string): PathAwareSignal<unknown> | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ModelGroupNode } from './model-nodes';
|
|
2
2
|
import { FormModel, ModelArray } from './types';
|
|
3
|
-
export declare const rootByFacade: WeakMap<object,
|
|
4
|
-
export declare function makeFormModel(group:
|
|
3
|
+
export declare const rootByFacade: WeakMap<object, ModelGroupNode>;
|
|
4
|
+
export declare function makeFormModel(group: ModelGroupNode): any;
|
|
5
5
|
/**
|
|
6
6
|
* Value-фасад для ручки {@link modelOf}: массив → {@link ModelArray}, объект → под-модель
|
|
7
7
|
* {@link FormModel}.
|
|
@@ -39,13 +39,6 @@ export type ModelOf<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelAr
|
|
|
39
39
|
export declare function modelOf<V extends object | null | undefined>(handle: {
|
|
40
40
|
peek(): V;
|
|
41
41
|
}): ModelOf<V>;
|
|
42
|
-
/**
|
|
43
|
-
* Ручка `$` по value-фасаду: под-модель → её `model.$`, фасад массива → узел-массив дерева `$`.
|
|
44
|
-
* Для всего остального — `undefined`.
|
|
45
|
-
*
|
|
46
|
-
* @internal
|
|
47
|
-
*/
|
|
48
|
-
export declare function signalsOf(facade: unknown): object | undefined;
|
|
49
42
|
/**
|
|
50
43
|
* Ручка массива по привязке: сама ручка `model.$.<массив>` либо value-фасад `model.<массив>`.
|
|
51
44
|
* Для всего остального — `undefined`.
|
|
@@ -61,9 +54,10 @@ export declare function arrayHandleOf(binding: unknown): object | undefined;
|
|
|
61
54
|
*/
|
|
62
55
|
export declare function groupHandleOf(binding: unknown): object | undefined;
|
|
63
56
|
/**
|
|
64
|
-
* Запасной шаблон нового элемента массива —
|
|
65
|
-
*
|
|
66
|
-
* данных без кода.
|
|
57
|
+
* Запасной шаблон нового элемента массива — от того, кто строит модель извне: сборка формы
|
|
58
|
+
* передаёт сюда `initialValue` узла-массива. Действует, только если модель своего шаблона не
|
|
59
|
+
* объявила (`arrayOf`): так живут формы, чья модель строится из данных без кода. Что это узел
|
|
60
|
+
* схемы, модель не знает.
|
|
67
61
|
*
|
|
68
62
|
* @internal
|
|
69
63
|
*/
|
|
@@ -71,9 +65,20 @@ export declare function provideArrayBlank(handle: unknown, initialValue: unknown
|
|
|
71
65
|
/**
|
|
72
66
|
* Value-фасад модели (под-модель или фасад массива), а не обычный объект?
|
|
73
67
|
*
|
|
74
|
-
* Обходчикам чужих
|
|
75
|
-
* реактивное чтение значений модели.
|
|
68
|
+
* Обходчикам чужих деревьев, в которых могут лежать фасады модели, в фасад спускаться нельзя:
|
|
69
|
+
* чтение его ключей — это реактивное чтение значений модели.
|
|
76
70
|
*
|
|
77
71
|
* @internal
|
|
78
72
|
*/
|
|
73
|
+
/**
|
|
74
|
+
* Точка отсчёта модели по ручке, к которой привязана нода формы: сигналу листа или фасаду
|
|
75
|
+
* массива. К ней сброс формы (`form.reset()`) возвращает ноду — к тому же, к чему `model.reset()`
|
|
76
|
+
* возвращает модель: последний `captureInitial` либо значение создания модели.
|
|
77
|
+
*
|
|
78
|
+
* `undefined` — ручка не из модели (нода над собственным сигналом): сбрасывать её не к чему,
|
|
79
|
+
* кроме значения, с которым она создана. @internal
|
|
80
|
+
*/
|
|
81
|
+
export declare function referenceOf(handle: unknown): {
|
|
82
|
+
readonly value: unknown;
|
|
83
|
+
} | undefined;
|
|
79
84
|
export declare function isModelFacade(value: unknown): boolean;
|
|
@@ -68,6 +68,38 @@ export declare function safeCallback<TArgs extends unknown[]>(callback: (...args
|
|
|
68
68
|
* ```
|
|
69
69
|
*/
|
|
70
70
|
export declare function runOutsideEffect(fn: () => void | Promise<void>): () => void;
|
|
71
|
+
/**
|
|
72
|
+
* Дождаться, пока отработают отложенные записи текущего тика — вместе с теми, что они породили
|
|
73
|
+
* (цепочка расчётов: каждая запись будит следующий расчёт, а тот снова пишет отложенно).
|
|
74
|
+
*
|
|
75
|
+
* Ждёт только микротаски: запись после таймера (debounce) или после `await` в async-callback сюда не
|
|
76
|
+
* входит — это уже не «тот же тик».
|
|
77
|
+
*
|
|
78
|
+
* Счётчик общий для рантайма, а не для одной модели: отложенные записи ставит {@link runOutsideEffect},
|
|
79
|
+
* а он моделей не различает. Нужен проверкам «по действию» — отправке и переходу шага: правила должны
|
|
80
|
+
* увидеть модель после записей поведения того же тика (колбэк `onChange`, сброс `resetOnDisable`).
|
|
81
|
+
*
|
|
82
|
+
* @returns Промис ожидания; `undefined` — отложенных записей сейчас нет, ждать нечего.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* model.loanType = 'mortgage'; // поведение допишет зависимые поля на микротаске
|
|
87
|
+
* await deferredWritesSettled();
|
|
88
|
+
* const values = model.get(); // уже с записями поведения
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function deferredWritesSettled(): Promise<void> | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Отложенные записи одной операции: задание выполняется на микротаске ({@link runOutsideEffect}),
|
|
94
|
+
* а `cancelAll` отменяет все ещё не выполненные — при снятии операции (запись после cleanup не
|
|
95
|
+
* случится) или когда новое задание делает ожидающие устаревшими (побеждает последнее).
|
|
96
|
+
*
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
export declare function deferredWrites(): {
|
|
100
|
+
schedule(job: () => void): void;
|
|
101
|
+
cancelAll(): void;
|
|
102
|
+
};
|
|
71
103
|
/**
|
|
72
104
|
* Создает версию callback с поддержкой debounce, безопасную для effect
|
|
73
105
|
*
|
|
@@ -76,7 +108,7 @@ export declare function runOutsideEffect(fn: () => void | Promise<void>): () =>
|
|
|
76
108
|
* async-rejection) маршрутизируются dev-логгером (logDeferError).
|
|
77
109
|
*
|
|
78
110
|
* @param callback - Функция для выполнения
|
|
79
|
-
* @param withDebounce -
|
|
111
|
+
* @param withDebounce - Обёртка, откладывающая вызов (debounce)
|
|
80
112
|
* @returns Обёрнутая функция
|
|
81
113
|
*
|
|
82
114
|
* @example
|
package/dist/model/types.d.ts
CHANGED
|
@@ -14,6 +14,33 @@ export type PathAwareSignal<T> = Signal<T> & {
|
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
16
|
type Opaque = Date | File | Blob;
|
|
17
|
+
/**
|
|
18
|
+
* Значение одного ключа в {@link DeepPartial}: вложенная группа — частично, массив и спец-объект
|
|
19
|
+
* (Date/File/Blob) — целиком.
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
type DeepPartialValue<V> = NonNullable<V> extends ReadonlyArray<unknown> | Opaque ? V : NonNullable<V> extends object ? DeepPartial<NonNullable<V>> | Extract<V, null | undefined> : V;
|
|
23
|
+
/**
|
|
24
|
+
* Частичное значение модели для {@link ModelApi.patch}: любой ключ можно опустить на любой
|
|
25
|
+
* глубине вложенных групп. Массив передаётся целиком — он заменяется, а не сливается.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* type Profile = { name: string; address: { city: string; street: string }; tags: string[] };
|
|
30
|
+
* const update: DeepPartial<Profile> = { address: { city: 'Казань' } };
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @group Model
|
|
34
|
+
*/
|
|
35
|
+
export type DeepPartial<T> = {
|
|
36
|
+
[K in keyof T]?: DeepPartialValue<T[K]>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* `null` входит в тип `V`. Без `strictNullChecks` у потребителя `null` подходит любому типу —
|
|
40
|
+
* тогда признак ложен, и тип контейнера остаётся фасадом, как без `null`.
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
type IncludesNull<V> = null extends string ? false : null extends V ? true : false;
|
|
17
44
|
/**
|
|
18
45
|
* Фасад элемента массива при обходе (`at`/`map`/`forEach`): массив → {@link ModelArray},
|
|
19
46
|
* Opaque (Date/File/Blob) и примитив → значение, объект → под-модель {@link FormModel}.
|
|
@@ -21,7 +48,7 @@ type Opaque = Date | File | Blob;
|
|
|
21
48
|
* чтобы `files: File[]`/`dates: Date[]`/вложенные массивы не мис-типизировались как `FormModel`.
|
|
22
49
|
* @internal
|
|
23
50
|
*/
|
|
24
|
-
type ModelArrayItem<U> = NonNullable<U> extends ReadonlyArray<infer E> ? ModelArray<E> : NonNullable<U> extends Opaque ? U : NonNullable<U> extends object ? FormModel<NonNullable<U>> : U;
|
|
51
|
+
type ModelArrayItem<U> = IncludesNull<U> extends true ? U : NonNullable<U> extends ReadonlyArray<infer E> ? ModelArray<E> : NonNullable<U> extends Opaque ? U : NonNullable<U> extends object ? FormModel<NonNullable<U>> : U;
|
|
25
52
|
/**
|
|
26
53
|
* Значение поля в value-доступе модели:
|
|
27
54
|
* - массив → {@link ModelArray}
|
|
@@ -29,10 +56,13 @@ type ModelArrayItem<U> = NonNullable<U> extends ReadonlyArray<infer E> ? ModelAr
|
|
|
29
56
|
* - объект → под-модель {@link FormModel} (value-доступ + `.$`-сигналы + API get/set/patch/…);
|
|
30
57
|
* промоутится рантаймом (`makeFormModel`); сигналы идентичны `model.$.<path>`
|
|
31
58
|
* - примитив → значение
|
|
59
|
+
* - тип с `null` (`File[] | null`, `Address | null`) → значение как есть, без фасада: поле,
|
|
60
|
+
* которое бывает `null`, начинают с `null`, а такое поле в рантайме — лист (значение целиком).
|
|
61
|
+
* Контейнер, созданный из `[]` или `{}`, стал бы фасадом — nullable-контейнер начинают с `null`.
|
|
32
62
|
*
|
|
33
63
|
* @group Model
|
|
34
64
|
*/
|
|
35
|
-
export type ModelValue<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArray<U> : NonNullable<V> extends Opaque ? V : NonNullable<V> extends object ? FormModel<NonNullable<V>> : V;
|
|
65
|
+
export type ModelValue<V> = IncludesNull<V> extends true ? V : NonNullable<V> extends ReadonlyArray<infer U> ? ModelArray<U> : NonNullable<V> extends Opaque ? V : NonNullable<V> extends object ? FormModel<NonNullable<V>> : V;
|
|
36
66
|
/**
|
|
37
67
|
* Карта value-полей объекта (value-половина {@link FormModel}): поля доступны как обычные свойства
|
|
38
68
|
* (чтение реактивно внутри `effect`/`computed`, запись — присваиванием). Вложенные объекты-поля
|
|
@@ -80,6 +110,8 @@ export interface ModelArray<U> {
|
|
|
80
110
|
forEach(fn: (item: ModelArrayItem<U>, index: number) => void): void;
|
|
81
111
|
/** Снимок массива значений (без подписки). */
|
|
82
112
|
toArray(): U[];
|
|
113
|
+
/** Обход `for…of` / spread — те же элементы, что у {@link forEach}. */
|
|
114
|
+
[Symbol.iterator](): IterableIterator<ModelArrayItem<U>>;
|
|
83
115
|
/** Индексный value-доступ. */
|
|
84
116
|
[index: number]: ModelValue<U>;
|
|
85
117
|
}
|
|
@@ -115,13 +147,13 @@ export type ModelGroupSignals<T> = ModelSignals<T> & ContainerSignal<T, keyof T>
|
|
|
115
147
|
* push/removeAt/move).
|
|
116
148
|
*
|
|
117
149
|
* В отличие от группы, узел ЗАПИСЫВАЕМ: `model.$.tags.value = ['a']` заменяет массив целиком. Это
|
|
118
|
-
* делает его {@link PathAwareSignal} — к нему привязывают поле (`{
|
|
150
|
+
* делает его {@link PathAwareSignal} — к нему привязывают поле (`{ model: model.$.tags, component }`),
|
|
119
151
|
* правило (`validate(model.$.tags, …)`) и поведение, как к листу. Так массив служит ОДНИМ значением
|
|
120
152
|
* поля: мультивыбор, теги, список файлов.
|
|
121
153
|
*
|
|
122
|
-
* `V` — объявленный тип поля, включая `
|
|
123
|
-
*
|
|
124
|
-
*
|
|
154
|
+
* `V` — объявленный тип поля, включая `undefined`. Поле с `null` в типе (`string[] | null`) — не
|
|
155
|
+
* узел-массив, а лист {@link PathAwareSignal}: его начинают с `null`, и `length` с индексами у него
|
|
156
|
+
* в рантайме нет (см. {@link ModelValue}).
|
|
125
157
|
*
|
|
126
158
|
* @group Model
|
|
127
159
|
*/
|
|
@@ -135,10 +167,11 @@ export type ModelArraySignals<U, V> = Signal<V> & {
|
|
|
135
167
|
* Узел дерева сигналов `model.$`: массив → {@link ModelArraySignals}, Opaque (Date/File/Blob)
|
|
136
168
|
* и примитив → {@link PathAwareSignal}, объект → {@link ModelGroupSignals}. Для примитивных
|
|
137
169
|
* и Opaque массивов элемент — сам сигнал листа (а не под-дерево), поэтому `model.$.tags[0]` — это
|
|
138
|
-
* `PathAwareSignal<string>`, а не `ModelGroupSignals<never>`.
|
|
170
|
+
* `PathAwareSignal<string>`, а не `ModelGroupSignals<never>`. Тип с `null` — лист
|
|
171
|
+
* {@link PathAwareSignal}: такое поле начинают с `null`, и под-сигналов у него нет.
|
|
139
172
|
* @internal
|
|
140
173
|
*/
|
|
141
|
-
type ModelSignalNode<V> = NonNullable<V> extends ReadonlyArray<infer U> ? ModelArraySignals<U, V> : NonNullable<V> extends Opaque ? PathAwareSignal<V> : NonNullable<V> extends object ? ModelGroupSignals<NonNullable<V>> : PathAwareSignal<V>;
|
|
174
|
+
type ModelSignalNode<V> = IncludesNull<V> extends true ? PathAwareSignal<V> : NonNullable<V> extends ReadonlyArray<infer U> ? ModelArraySignals<U, V> : NonNullable<V> extends Opaque ? PathAwareSignal<V> : NonNullable<V> extends object ? ModelGroupSignals<NonNullable<V>> : PathAwareSignal<V>;
|
|
142
175
|
/**
|
|
143
176
|
* API уровня модели (доступно на корне, под-моделях вложенных объектов-групп и элементов массива).
|
|
144
177
|
*
|
|
@@ -159,18 +192,35 @@ export interface ModelApi<T> {
|
|
|
159
192
|
* Полная установка значений: принимает объект целиком (все ключи `T`) и записывает их в модель.
|
|
160
193
|
* Производными полями (цели `compute`) владеет compute — их значения из payload игнорируются.
|
|
161
194
|
* Не меняет initial-снимок. Для частичного обновления (только переданные ключи) — {@link ModelApi.patch}.
|
|
195
|
+
*
|
|
196
|
+
* Если в объекте не хватает ключей (значение пришло без типа — например, с сервера), эти поля
|
|
197
|
+
* остаются прежними, а в dev выводится предупреждение со списком.
|
|
162
198
|
*/
|
|
163
199
|
set(value: T): void;
|
|
164
200
|
/**
|
|
165
201
|
* Частичное слияние значений (load/patch с сервера): обновляет только переданные ключи,
|
|
166
|
-
* отсутствующие ключи НЕ трогаются.
|
|
202
|
+
* отсутствующие ключи НЕ трогаются. Вложенные группы сливаются так же, массив заменяется
|
|
203
|
+
* целиком. Не меняет initial-снимок.
|
|
204
|
+
*/
|
|
205
|
+
patch(value: DeepPartial<T>): void;
|
|
206
|
+
/**
|
|
207
|
+
* Отличаются ли текущие значения от initial-снимка (value-diff). Производные поля — цели
|
|
208
|
+
* `compute` / `computeFrom` и поля с `markDerived` — не в счёт: их пишет поведение, а не
|
|
209
|
+
* пользователь, и форма без единой правки изменённой не считается.
|
|
167
210
|
*/
|
|
168
|
-
patch(value: Partial<T>): void;
|
|
169
|
-
/** Отличаются ли текущие значения от initial-снимка (value-diff). */
|
|
170
211
|
isDirty(): boolean;
|
|
171
|
-
/**
|
|
212
|
+
/**
|
|
213
|
+
* Сбросить значения к initial-снимку. Производные поля сброс не пишет, как `set` и `patch`:
|
|
214
|
+
* цель `compute` пересчитает поведение, когда вернутся её источники.
|
|
215
|
+
*
|
|
216
|
+
* Ноды формы не трогает: замечания погаснут при следующей проверке. Сброс для пользователя —
|
|
217
|
+
* `form.reset()`: та же точка отсчёта и чистые ноды.
|
|
218
|
+
*/
|
|
172
219
|
reset(): void;
|
|
173
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* Зафиксировать текущие значения как новый initial-снимок («точка отсчёта»). К нему возвращают
|
|
222
|
+
* и `model.reset()`, и `form.reset()`.
|
|
223
|
+
*/
|
|
174
224
|
captureInitial(): void;
|
|
175
225
|
/**
|
|
176
226
|
* Резолв строкового пути в сигнал (для error-routing/мостов): лист либо массив целиком
|
package/dist/model.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { c as s, e as r, a as l } from "./create-model-
|
|
2
|
-
import { a as o, d as c, i as
|
|
3
|
-
import { c as
|
|
1
|
+
import { c as s, e as r, a as l } from "./create-model-BvcbNPgL.js";
|
|
2
|
+
import { a as d, g as o, f as t, d as c, i as f, b as n, c as m, m as u, r as p, s as b, e as g, u as h } from "./safe-effect-BbtX3KuN.js";
|
|
3
|
+
import { c as k, a as v, r as D, b as F, s as x, t as y, w as C } from "./behaviors-value-HA_Q03hc.js";
|
|
4
4
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
d as arrayOf,
|
|
6
|
+
k as computeFrom,
|
|
7
|
+
v as copyFrom,
|
|
8
8
|
s as createModel,
|
|
9
|
+
o as deepEqual,
|
|
10
|
+
t as deferredWritesSettled,
|
|
9
11
|
r as eachLeafSignal,
|
|
10
12
|
l as eachValueSignal,
|
|
11
13
|
c as isDerived,
|
|
12
|
-
|
|
14
|
+
f as isModelContainerSignal,
|
|
13
15
|
n as isValueSignal,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
m as markDerived,
|
|
17
|
+
u as modelOf,
|
|
18
|
+
D as resetWhen,
|
|
19
|
+
F as revalidateWhen,
|
|
20
|
+
p as runOutsideEffect,
|
|
21
|
+
b as safeCallback,
|
|
22
|
+
g as safeDebouncedCallback,
|
|
23
|
+
x as syncFields,
|
|
24
|
+
y as transformValue,
|
|
25
|
+
h as unmarkDerived,
|
|
26
|
+
C as watchField
|
|
25
27
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const n = Symbol.for("@reformer/core/form-node-kind"), e = Symbol.for("@reformer/core/form-node-raw");
|
|
2
|
+
function t(r) {
|
|
3
|
+
if (typeof r != "object" || r === null) return;
|
|
4
|
+
const o = r[n];
|
|
5
|
+
return o === "control" || o === "group" || o === "array" ? o : void 0;
|
|
6
|
+
}
|
|
7
|
+
function f(r) {
|
|
8
|
+
return r[e] ?? r;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
n as N,
|
|
12
|
+
e as R,
|
|
13
|
+
t as k,
|
|
14
|
+
f as r
|
|
15
|
+
};
|
|
@@ -41,7 +41,7 @@ export interface FieldAdapter {
|
|
|
41
41
|
strip?: string[];
|
|
42
42
|
/**
|
|
43
43
|
* Передавать ли контролу ноду формы пропом `control` (только рендерер). По умолчанию `false`:
|
|
44
|
-
*
|
|
44
|
+
* issues/touched обслуживает обёртка поля. Ставь `true`, если контрол сам вызывает
|
|
45
45
|
* `useFormControl(control)`.
|
|
46
46
|
*/
|
|
47
47
|
passControl?: boolean;
|
|
@@ -43,8 +43,11 @@ export declare function makeElementFieldHandle(el: RefObject<HTMLElement | null>
|
|
|
43
43
|
export declare function useFieldHandleRef<H = FieldHandle>(outerRef: Ref<H> | undefined): RefObject<unknown>;
|
|
44
44
|
/**
|
|
45
45
|
* Что публиковать потребителю по ref контрола: handle композита как есть, иначе (DOM-узел или
|
|
46
|
-
* ещё пусто) — базовый {@link FieldHandle}, читающий узел лениво из `inner`.
|
|
46
|
+
* ещё пусто) — базовый {@link FieldHandle}, читающий узел лениво из `inner`. С `cache` базовый
|
|
47
|
+
* handle создаётся один раз и дальше переиспользуется.
|
|
47
48
|
*
|
|
48
49
|
* @internal Экспортируется для юнит-тестов.
|
|
49
50
|
*/
|
|
50
|
-
export declare function resolveFieldHandle(inner: RefObject<unknown
|
|
51
|
+
export declare function resolveFieldHandle(inner: RefObject<unknown>, cache?: {
|
|
52
|
+
current: FieldHandle | null;
|
|
53
|
+
}): unknown;
|
|
@@ -12,7 +12,7 @@ import { FormBundle } from '../../form/form-bundle';
|
|
|
12
12
|
export declare const FormBundleContext: import('react').Context<FormBundle<any> | null>;
|
|
13
13
|
/**
|
|
14
14
|
* Бандл сборки, внутри которой отрисован компонент; `null` — компонент стоит вне рендерера
|
|
15
|
-
* (например, визард в разметке React-руками получает форму и
|
|
15
|
+
* (например, визард в разметке React-руками получает форму и поток шагов пропсами).
|
|
16
16
|
*
|
|
17
17
|
* @typeParam T - Форма данных модели.
|
|
18
18
|
*
|
|
@@ -20,7 +20,7 @@ export declare const FormBundleContext: import('react').Context<FormBundle<any>
|
|
|
20
20
|
* ```tsx
|
|
21
21
|
* function StepFooter() {
|
|
22
22
|
* const bundle = useFormBundleContext<CreditForm>();
|
|
23
|
-
* return <button onClick={() => bundle?.
|
|
23
|
+
* return <button onClick={() => void bundle?.validate()}>Проверить</button>;
|
|
24
24
|
* }
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ValidationIssue } from '../../../form/types/index';
|
|
2
2
|
/**
|
|
3
|
-
* Состояние поля формы, возвращаемое хуком {@link useFormControl} для {@link
|
|
3
|
+
* Состояние поля формы, возвращаемое хуком {@link useFormControl} для {@link FormControl}.
|
|
4
4
|
*
|
|
5
5
|
* Содержит реактивные данные поля: значение, состояние валидации, флаги взаимодействия
|
|
6
6
|
* и пользовательские props для компонентов.
|
|
@@ -10,12 +10,12 @@ import { ValidationError } from '../../../form/types/index';
|
|
|
10
10
|
* @example Базовое использование
|
|
11
11
|
* ```tsx
|
|
12
12
|
* interface Props {
|
|
13
|
-
* control:
|
|
13
|
+
* control: FormControl<string>;
|
|
14
14
|
* }
|
|
15
15
|
*
|
|
16
16
|
* function TextField({ control }: Props) {
|
|
17
17
|
* const state = useFormControl(control);
|
|
18
|
-
* const message =
|
|
18
|
+
* const message = useIssueMessage(); // из '@reformer/core/react'
|
|
19
19
|
*
|
|
20
20
|
* return (
|
|
21
21
|
* <div>
|
|
@@ -24,8 +24,8 @@ import { ValidationError } from '../../../form/types/index';
|
|
|
24
24
|
* disabled={state.disabled}
|
|
25
25
|
* onChange={e => control.setValue(e.target.value)}
|
|
26
26
|
* />
|
|
27
|
-
* {state.
|
|
28
|
-
* <span className="error">{message(state.
|
|
27
|
+
* {state.shouldShowIssues && state.issues[0] && (
|
|
28
|
+
* <span className="error">{message(state.issues[0])}</span>
|
|
29
29
|
* )}
|
|
30
30
|
* </div>
|
|
31
31
|
* );
|
|
@@ -89,22 +89,22 @@ export interface FieldControlState<T> {
|
|
|
89
89
|
*
|
|
90
90
|
* @example
|
|
91
91
|
* ```tsx
|
|
92
|
-
* const {
|
|
93
|
-
* const message =
|
|
92
|
+
* const { issues } = useFormControl(field);
|
|
93
|
+
* const message = useIssueMessage(); // из '@reformer/core/react'
|
|
94
94
|
*
|
|
95
95
|
* return (
|
|
96
96
|
* <ul className="error-list">
|
|
97
|
-
* {
|
|
97
|
+
* {issues.map((error, i) => (
|
|
98
98
|
* <li key={i}>{message(error)}</li>
|
|
99
99
|
* ))}
|
|
100
100
|
* </ul>
|
|
101
101
|
* );
|
|
102
102
|
* ```
|
|
103
103
|
*/
|
|
104
|
-
|
|
104
|
+
issues: ValidationIssue[];
|
|
105
105
|
/**
|
|
106
106
|
* Флаг валидности поля.
|
|
107
|
-
* `true` когда поле прошло все валидации (
|
|
107
|
+
* `true` когда поле прошло все валидации (issues.length === 0).
|
|
108
108
|
*
|
|
109
109
|
* @example
|
|
110
110
|
* ```tsx
|
|
@@ -118,7 +118,7 @@ export interface FieldControlState<T> {
|
|
|
118
118
|
valid: boolean;
|
|
119
119
|
/**
|
|
120
120
|
* Флаг невалидности поля.
|
|
121
|
-
* `true` когда есть ошибки валидации (
|
|
121
|
+
* `true` когда есть ошибки валидации (issues.length > 0).
|
|
122
122
|
* Противоположность {@link valid}.
|
|
123
123
|
*
|
|
124
124
|
* @example
|
|
@@ -148,28 +148,44 @@ export interface FieldControlState<T> {
|
|
|
148
148
|
*/
|
|
149
149
|
touched: boolean;
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
151
|
+
* Показывать ли состояние ошибки — `aria-invalid`, рамку: поле невалидно и его трогали или
|
|
152
|
+
* меняли. Предупреждение (`severity: 'warning'`) поле невалидным не делает, поэтому для текстов
|
|
153
|
+
* замечаний есть {@link FieldControlState.shouldShowIssues}.
|
|
153
154
|
*
|
|
154
155
|
* @example
|
|
155
156
|
* ```tsx
|
|
156
|
-
* const { shouldShowError
|
|
157
|
-
*
|
|
157
|
+
* const { shouldShowError } = useFormControl(field);
|
|
158
|
+
*
|
|
159
|
+
* return <input {...props} aria-invalid={shouldShowError} />;
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
shouldShowError: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Показывать ли тексты замечаний: у поля есть замечания — ошибки или предупреждения, — и его
|
|
165
|
+
* трогали или меняли.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```tsx
|
|
169
|
+
* const { shouldShowError, shouldShowIssues, issues } = useFormControl(field);
|
|
170
|
+
* const message = useIssueMessage(); // из '@reformer/core/react'
|
|
158
171
|
*
|
|
159
172
|
* return (
|
|
160
173
|
* <div>
|
|
161
|
-
* <input {...props} />
|
|
162
|
-
* {
|
|
163
|
-
*
|
|
164
|
-
*
|
|
174
|
+
* <input {...props} aria-invalid={shouldShowError} />
|
|
175
|
+
* {shouldShowIssues &&
|
|
176
|
+
* issues.map((issue) => (
|
|
177
|
+
* <span key={issue.code} className={issue.severity === 'warning' ? 'warning' : 'error'}>
|
|
178
|
+
* {message(issue)}
|
|
179
|
+
* </span>
|
|
180
|
+
* ))}
|
|
165
181
|
* </div>
|
|
166
182
|
* );
|
|
167
183
|
* ```
|
|
168
184
|
*/
|
|
169
|
-
|
|
185
|
+
shouldShowIssues: boolean;
|
|
170
186
|
/**
|
|
171
187
|
* Пользовательские props для передачи в UI-компоненты.
|
|
172
|
-
* Устанавливаются через {@link
|
|
188
|
+
* Устанавливаются через {@link FormControl.updateComponentProps}.
|
|
173
189
|
*
|
|
174
190
|
* @example
|
|
175
191
|
* ```tsx
|
|
@@ -210,7 +226,7 @@ export interface FieldControlState<T> {
|
|
|
210
226
|
dirty: boolean;
|
|
211
227
|
}
|
|
212
228
|
/**
|
|
213
|
-
* Состояние массива формы, возвращаемое хуком {@link useFormControl} для {@link
|
|
229
|
+
* Состояние массива формы, возвращаемое хуком {@link useFormControl} для {@link FormArray}.
|
|
214
230
|
*
|
|
215
231
|
* Содержит реактивные данные массива: значения элементов, длину, состояние валидации
|
|
216
232
|
* и флаги взаимодействия.
|
|
@@ -225,7 +241,7 @@ export interface FieldControlState<T> {
|
|
|
225
241
|
* }
|
|
226
242
|
*
|
|
227
243
|
* interface Props {
|
|
228
|
-
* control:
|
|
244
|
+
* control: FormArray<Phone>;
|
|
229
245
|
* }
|
|
230
246
|
*
|
|
231
247
|
* function PhoneList({ control }: Props) {
|
|
@@ -235,7 +251,7 @@ export interface FieldControlState<T> {
|
|
|
235
251
|
* <div>
|
|
236
252
|
* {control.map((item, index) => (
|
|
237
253
|
* <PhoneItem
|
|
238
|
-
* key={
|
|
254
|
+
* key={control.keyAt(index)}
|
|
239
255
|
* control={item}
|
|
240
256
|
* onRemove={() => control.removeAt(index)}
|
|
241
257
|
* />
|
|
@@ -247,7 +263,7 @@ export interface FieldControlState<T> {
|
|
|
247
263
|
* Add Phone
|
|
248
264
|
* </button>
|
|
249
265
|
*
|
|
250
|
-
* {!valid && <p className="error">Please fix phone
|
|
266
|
+
* {!valid && <p className="error">Please fix phone issues</p>}
|
|
251
267
|
* </div>
|
|
252
268
|
* );
|
|
253
269
|
* }
|
|
@@ -293,23 +309,24 @@ export interface ArrayControlState<T> {
|
|
|
293
309
|
*/
|
|
294
310
|
pending: boolean;
|
|
295
311
|
/**
|
|
296
|
-
*
|
|
297
|
-
* Не включает ошибки отдельных элементов.
|
|
312
|
+
* Замечания массива: собственные (правила на ручке массива) и замечания всех его строк.
|
|
298
313
|
*
|
|
299
314
|
* @example
|
|
300
315
|
* ```tsx
|
|
301
|
-
* //
|
|
302
|
-
*
|
|
303
|
-
* validator: (phones) => phones.length >= 1,
|
|
304
|
-
* message: 'At least one phone required'
|
|
305
|
-
* });
|
|
316
|
+
* // Правило массива — в схеме валидации
|
|
317
|
+
* validate(model.$.phones, [minLength(1, { code: 'phones.minLength' })]);
|
|
306
318
|
*
|
|
307
319
|
* // В компоненте
|
|
308
|
-
* const {
|
|
309
|
-
* //
|
|
320
|
+
* const { issues } = useFormControl(phonesArray);
|
|
321
|
+
* // для пустого массива issues содержит замечание с кодом 'phones.minLength'
|
|
310
322
|
* ```
|
|
311
323
|
*/
|
|
312
|
-
|
|
324
|
+
issues: ValidationIssue[];
|
|
325
|
+
/**
|
|
326
|
+
* Собственные замечания массива — только правила на его ручке (`minLength` и т.п.), без
|
|
327
|
+
* замечаний строк. Их показывает ошибка уровня массива (`FieldArray.Error`).
|
|
328
|
+
*/
|
|
329
|
+
ownIssues: ValidationIssue[];
|
|
313
330
|
/**
|
|
314
331
|
* Флаг валидности массива и всех его элементов.
|
|
315
332
|
* `true` только когда массив и все вложенные элементы валидны.
|
|
@@ -327,7 +344,9 @@ export interface ArrayControlState<T> {
|
|
|
327
344
|
touched: boolean;
|
|
328
345
|
/**
|
|
329
346
|
* Флаг изменения.
|
|
330
|
-
* `true`
|
|
347
|
+
* `true` после правки любого элемента или изменения состава через форму (`push`, `removeAt`,
|
|
348
|
+
* `move`, `setValue`…). Как у поля, запись в модель (загрузка данных) и операция, которая состав
|
|
349
|
+
* не поменяла, изменением не считаются; снимает `reset`.
|
|
331
350
|
*
|
|
332
351
|
* @example
|
|
333
352
|
* ```tsx
|
|
@@ -344,7 +363,7 @@ export interface ArrayControlState<T> {
|
|
|
344
363
|
dirty: boolean;
|
|
345
364
|
/**
|
|
346
365
|
* Флаг отключения массива.
|
|
347
|
-
* `true` когда массив отключён (`
|
|
366
|
+
* `true` когда массив отключён (`FormArray.disable()`), в том числе через
|
|
348
367
|
* распространение disable от родительской группы.
|
|
349
368
|
*
|
|
350
369
|
* Используйте для отключения UI-действий добавления/удаления, когда массив
|