@reformer/core 11.0.0-beta.7 → 11.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -68
- package/dist/behaviors-value-HA_Q03hc.js +108 -0
- package/dist/behaviors.js +382 -217
- package/dist/create-model-BvcbNPgL.js +30 -0
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +42 -8
- package/dist/form/behaviors/context.d.ts +37 -4
- package/dist/form/behaviors/index.d.ts +6 -4
- package/dist/form/behaviors/internals.d.ts +9 -3
- package/dist/form/behaviors/node.d.ts +25 -2
- package/dist/form/behaviors/operators.d.ts +17 -9
- package/dist/form/behaviors/schema-operators.d.ts +78 -0
- package/dist/form/behaviors/types.d.ts +25 -5
- package/dist/form/create-form.d.ts +57 -70
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +153 -0
- package/dist/form/form-config-base.d.ts +46 -0
- package/dist/form/form-level.d.ts +121 -0
- package/dist/form/form-proxy-builder.d.ts +6 -22
- package/dist/form/index.d.ts +20 -20
- package/dist/form/nodes/form-array-node.d.ts +100 -0
- package/dist/form/nodes/form-control-node.d.ts +73 -0
- package/dist/form/nodes/form-group-node.d.ts +66 -0
- package/dist/form/nodes/node-kind.d.ts +29 -0
- package/dist/form/nodes/state/availability.d.ts +6 -0
- package/dist/form/nodes/state/component.d.ts +6 -0
- package/dist/form/nodes/state/index.d.ts +12 -0
- package/dist/form/nodes/state/interaction.d.ts +13 -0
- package/dist/form/nodes/state/issues.d.ts +9 -0
- package/dist/form/nodes/state/pending.d.ts +6 -0
- package/dist/form/schema/compile.d.ts +59 -0
- package/dist/form/schema/compiled.d.ts +143 -0
- package/dist/form/schema/row-key.d.ts +11 -0
- package/dist/form/schema-controller.d.ts +153 -0
- package/dist/form/schema-subtree.d.ts +28 -0
- package/dist/form/signal-node-registry.d.ts +27 -22
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +19 -92
- package/dist/form/types/field-config.d.ts +27 -0
- package/dist/form/types/index.d.ts +5 -57
- package/dist/form/types/nodes.d.ts +159 -0
- package/dist/form/types/schema-node.d.ts +147 -46
- package/dist/form/types/validation-schema.d.ts +23 -27
- package/dist/form/validation/context.d.ts +46 -6
- package/dist/form/validation/index.d.ts +9 -8
- package/dist/form/validation/operators.d.ts +71 -17
- package/dist/form/validation/run.d.ts +93 -18
- package/dist/form/validation/strategy.d.ts +41 -13
- package/dist/form/validation/types.d.ts +74 -19
- package/dist/form/validators/date-utils.d.ts +13 -1
- package/dist/form/validators/email.d.ts +8 -7
- package/dist/form/validators/file-type.d.ts +8 -7
- package/dist/form/validators/file-utils.d.ts +2 -1
- package/dist/form/validators/future-date.d.ts +8 -7
- package/dist/form/validators/index.d.ts +1 -1
- package/dist/form/validators/integer.d.ts +8 -7
- package/dist/form/validators/is-date.d.ts +11 -9
- package/dist/form/validators/is-number.d.ts +11 -9
- package/dist/form/validators/max-age.d.ts +13 -9
- package/dist/form/validators/max-date.d.ts +8 -7
- package/dist/form/validators/max-file-size.d.ts +8 -7
- package/dist/form/validators/max-files.d.ts +8 -7
- package/dist/form/validators/max-length.d.ts +14 -7
- package/dist/form/validators/max-total-file-size.d.ts +8 -7
- package/dist/form/validators/max.d.ts +10 -8
- package/dist/form/validators/min-age.d.ts +12 -9
- package/dist/form/validators/min-date.d.ts +8 -7
- package/dist/form/validators/min-file-size.d.ts +8 -7
- package/dist/form/validators/min-files.d.ts +11 -9
- package/dist/form/validators/min-length.d.ts +14 -7
- package/dist/form/validators/min.d.ts +10 -8
- package/dist/form/validators/multiple-of.d.ts +10 -8
- package/dist/form/validators/non-negative.d.ts +8 -7
- package/dist/form/validators/non-zero.d.ts +8 -7
- package/dist/form/validators/past-date.d.ts +8 -7
- package/dist/form/validators/pattern.d.ts +9 -8
- package/dist/form/validators/phone.d.ts +8 -7
- package/dist/form/validators/required.d.ts +11 -9
- package/dist/form/validators/url.d.ts +20 -13
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +1 -1
- package/dist/i18n/en.json.d.ts +5 -1
- package/dist/i18n/locale-store.d.ts +7 -0
- package/dist/i18n/message-format.d.ts +24 -4
- package/dist/i18n/ru.json.d.ts +5 -1
- package/dist/i18n/translator.d.ts +11 -0
- package/dist/i18n/validation-message.d.ts +42 -7
- package/dist/i18n.d.ts +7 -12
- package/dist/i18n.js +66 -283
- package/dist/index.d.ts +16 -7
- package/dist/index.js +958 -2798
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.js +1 -1
- package/dist/locale/en.json +4 -1
- package/dist/locale/ru.js +4 -1
- package/dist/locale/ru.json +4 -1
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.js +1 -1
- package/dist/model/behaviors-value.d.ts +24 -6
- package/dist/model/create-model.d.ts +2 -2
- package/dist/model/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +16 -5
- package/dist/model/model-nodes.d.ts +138 -8
- package/dist/model/model-signals-proxy.d.ts +11 -5
- package/dist/model/model-value-proxy.d.ts +84 -3
- package/dist/model/safe-effect.d.ts +33 -1
- package/dist/model/types.d.ts +70 -17
- package/dist/model.js +21 -17
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
- package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
- package/dist/platforms/react/form-bundle-context.d.ts +27 -0
- package/dist/platforms/react/hooks/types.d.ts +57 -38
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
- package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
- package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
- package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
- package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +6 -1
- package/dist/platforms/react/i18n/hooks.d.ts +16 -10
- package/dist/platforms/react/i18n/provider.d.ts +9 -2
- package/dist/platforms/react/index.d.ts +7 -4
- package/dist/react.d.ts +22 -0
- package/dist/react.js +505 -0
- package/dist/runtime-token.d.ts +1 -2
- package/dist/safe-effect-BbtX3KuN.js +643 -0
- package/dist/schema-controller-CpiO2JoK.js +143 -0
- package/dist/signals.d.ts +15 -9
- package/dist/signals.js +11 -6
- package/dist/strategy-CoFSemuP.js +375 -0
- package/dist/type-guards-D-N_5Ddh.js +19 -0
- package/dist/validation-issue-B9mjI8jz.js +7 -0
- package/dist/validation-message-Ced9MGxs.js +194 -0
- package/dist/validation.js +9 -8
- package/dist/validators/email.js +5 -5
- package/dist/validators/future-date.js +8 -8
- package/dist/validators/integer.js +3 -3
- package/dist/validators/is-date.js +4 -4
- package/dist/validators/is-number.js +4 -4
- package/dist/validators/max-age.js +8 -9
- package/dist/validators/max-date.js +8 -8
- package/dist/validators/max-length.js +7 -7
- package/dist/validators/max.js +4 -4
- package/dist/validators/min-age.js +10 -8
- package/dist/validators/min-date.js +3 -3
- package/dist/validators/min-length.js +5 -5
- package/dist/validators/min.js +4 -4
- package/dist/validators/multiple-of.js +11 -7
- package/dist/validators/non-negative.js +3 -3
- package/dist/validators/non-zero.js +3 -3
- package/dist/validators/past-date.js +8 -8
- package/dist/validators/pattern.js +5 -5
- package/dist/validators/phone.js +11 -9
- package/dist/validators/required.js +2 -2
- package/dist/validators/url.js +48 -12
- package/dist/validators.js +17 -17
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +111 -80
- package/docs/llms/03-api-signatures.md +368 -153
- package/docs/llms/04-common-patterns.md +58 -56
- package/docs/llms/05-common-mistakes.md +69 -44
- package/docs/llms/06-troubleshooting.md +14 -9
- package/docs/llms/07-complete-import.md +74 -43
- package/docs/llms/08-form-types.md +11 -4
- package/docs/llms/09-formschema.md +131 -83
- package/docs/llms/10-arrays.md +119 -69
- package/docs/llms/11-async-watchfield.md +2 -2
- package/docs/llms/13-multi-step.md +134 -63
- package/docs/llms/14-extended-mistakes.md +20 -17
- package/docs/llms/15-project-structure.md +221 -74
- package/docs/llms/16-ui-components.md +55 -42
- package/docs/llms/17-nonexistent-api.md +199 -32
- package/docs/llms/18-conditional-fields.md +66 -32
- package/docs/llms/19-reading-values.md +15 -3
- package/docs/llms/20-compute-vs-watch.md +16 -10
- package/docs/llms/21-array-operations.md +76 -26
- package/docs/llms/22-cycle-detection.md +1 -1
- package/docs/llms/23-copy-from.md +2 -2
- package/docs/llms/24-sync-fields.md +6 -5
- package/docs/llms/25-reset-when.md +14 -7
- package/docs/llms/26-transform-value.md +4 -4
- package/docs/llms/27-revalidate-when.md +16 -15
- package/docs/llms/28-submit-and-reset.md +276 -128
- package/docs/llms/29-async-preload.md +40 -30
- package/docs/llms/30-type-safety-recipes.md +105 -51
- package/docs/llms/31-async-validator-debounce.md +96 -53
- package/docs/llms/33-validation-strategy.md +178 -66
- package/docs/llms/34-i18n.md +69 -43
- package/docs/llms/35-unified-contract.md +307 -0
- package/docs/llms/36-issue-messages.md +284 -0
- package/docs/llms/37-compiled-schema.md +332 -0
- package/docs/llms/38-form-flow.md +397 -0
- package/llms-index.json +1 -1
- package/llms.txt +6968 -3278
- package/package.json +23 -4
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/create-model-B8U-COv-.js +0 -103
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/form/create-core-form.d.ts +0 -70
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -189
- package/dist/form/form-submitter.d.ts +0 -121
- package/dist/form/nodes/array-node.d.ts +0 -276
- package/dist/form/nodes/field-node.d.ts +0 -297
- package/dist/form/nodes/form-node.d.ts +0 -326
- package/dist/form/nodes/group-node.d.ts +0 -284
- package/dist/form/nodes/model-array-node.d.ts +0 -121
- package/dist/form/nodes/subscription-manager.d.ts +0 -180
- package/dist/form/status-machine.d.ts +0 -115
- package/dist/form/types/deep-schema.d.ts +0 -107
- package/dist/form/types/form-proxy.d.ts +0 -141
- package/dist/form/unique-id.d.ts +0 -45
- package/dist/form/validation/config.d.ts +0 -73
- package/dist/form/validation/error-handler.d.ts +0 -166
- package/dist/form/validators/validation-error.d.ts +0 -8
- package/dist/index-BrNkQj1y.js +0 -105
- package/dist/loader-Dv_Hl7JC.js +0 -216
- package/dist/model-signals-proxy-2vqS243J.js +0 -250
- package/dist/node-DGEFMTQu.js +0 -18
- package/dist/strategy-BXS_gBfz.js +0 -170
- package/dist/validation-error-B-U3RsV0.js +0 -11
package/dist/form/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Модуль `form` — доменный слой формы поверх реактивной модели (`model`).
|
|
3
3
|
*
|
|
4
|
-
* Ноды (value/touched/dirty/
|
|
5
|
-
*
|
|
4
|
+
* Ноды (value/touched/dirty/disabled/issues/componentProps) — контракты FormNode, FormControl,
|
|
5
|
+
* FormGroup, FormArray; сборка формы из модели по идентичности сигнала (`createForm`), уровень формы
|
|
6
|
+
* (проверка, отправка, поток шагов). Классы нод — внутренние.
|
|
6
7
|
* React-биндинги вынесены в `platforms/react` — этот слой от React не зависит.
|
|
7
8
|
* Schema-валидация — внешний контракт `@reformer/core/validation` (`validateModel`),
|
|
8
9
|
* роутит ошибки в ноды через реестр сигнал→нода. Зависит от `model` (form→model разрешено);
|
|
@@ -11,23 +12,22 @@
|
|
|
11
12
|
* @group Form
|
|
12
13
|
* @module form
|
|
13
14
|
*/
|
|
14
|
-
export {
|
|
15
|
-
export type {
|
|
16
|
-
export {
|
|
17
|
-
export { GroupNode } from './nodes/group-node';
|
|
18
|
-
export { ArrayNode } from './nodes/array-node';
|
|
19
|
-
export { ModelArrayNode } from './nodes/model-array-node';
|
|
20
|
-
export type { ModelArrayControl } from './nodes/model-array-node';
|
|
21
|
-
export { createForm, createFormFromModel } from './create-form';
|
|
15
|
+
export { createForm } from './form-bundle';
|
|
16
|
+
export type { CreateFormConfig, FormBundle, FormRender, ResolvedSchema, SchemaResolver, } from './form-bundle';
|
|
17
|
+
export { createFormFromModel, disposeForm } from './create-form';
|
|
22
18
|
export type { CreateFormFromModelArgs } from './create-form';
|
|
23
|
-
export {
|
|
24
|
-
export
|
|
25
|
-
export {
|
|
26
|
-
export type {
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
19
|
+
export { schemaSubtree } from './schema-subtree';
|
|
20
|
+
export { compileSchema, isCompiledNode } from './schema/compile';
|
|
21
|
+
export type { CompileSchemaOptions } from './schema/compile';
|
|
22
|
+
export type { CompiledSchema, CompiledNode, CompiledChild, CompiledFieldNode, CompiledArrayNode, CompiledPartNode, CompiledContainerNode, CompiledTextNode, SchemaNodeId, SchemaScopeId, SchemaTextPart, } from './schema/compiled';
|
|
23
|
+
export { createSchemaController, createSchemaScope, unknownSchemaSelectors, } from './schema-controller';
|
|
24
|
+
export type { SchemaController, SchemaScope, SchemaNodeControl, SchemaOverrideMaps, NodeLifecycleHooks, } from './schema-controller';
|
|
25
|
+
export type { CoreForm, CreateFormConfigBase } from './form-config-base';
|
|
26
|
+
export { createFormLevel } from './form-level';
|
|
27
|
+
export type { FormLevel, FormValidation, FormValidationState, FormFlowConfig, FlowStep, } from './form-level';
|
|
28
|
+
export { createSubmission } from './submission';
|
|
29
|
+
export type { ActionCheck, FormSubmission, SubmissionDependencies, SubmitResult, } from './submission';
|
|
30
|
+
export { createFormFlow } from './flow/flow';
|
|
31
|
+
export type { FormFlow, FormFlowOptions, FlowTransition, FlowStayReason } from './flow/flow';
|
|
29
32
|
export { registerSignalNode, getNodeForSignal } from './signal-node-registry';
|
|
30
|
-
export {
|
|
31
|
-
export { SubscriptionManager } from './nodes/subscription-manager';
|
|
32
|
-
export type { SubmittableForm, SubmitOptions, SubmitResult } from './form-submitter';
|
|
33
|
-
export { isFormNode, isFieldNode, isGroupNode, isArrayNode, getNodeType } from './type-guards';
|
|
33
|
+
export { isFormNode, isFormControl, isFormGroup, isFormArray } from './type-guards';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { ValidationIssue } from '../types/contracts';
|
|
3
|
+
import { FormArray, FormGroup } from '../types/nodes';
|
|
4
|
+
import { FormModel } from '../../model/types';
|
|
5
|
+
import { NODE_KIND, InternalFormNode } from './node-kind';
|
|
6
|
+
/** Реактивный массив модели в объёме, которым пользуется нода. */
|
|
7
|
+
export interface ModelArrayControl<TItem extends object> {
|
|
8
|
+
readonly length: number;
|
|
9
|
+
at(index: number): FormModel<TItem> | undefined;
|
|
10
|
+
push(item?: TItem): void;
|
|
11
|
+
insertAt(index: number, item?: TItem): void;
|
|
12
|
+
removeAt(index: number): void;
|
|
13
|
+
move(from: number, to: number): void;
|
|
14
|
+
swap(first: number, second: number): void;
|
|
15
|
+
clear(): void;
|
|
16
|
+
toArray(): TItem[];
|
|
17
|
+
}
|
|
18
|
+
export declare class FormArrayNode<T extends object> implements FormArray<T>, InternalFormNode<T[]> {
|
|
19
|
+
private readonly control;
|
|
20
|
+
readonly [NODE_KIND]: "array";
|
|
21
|
+
readonly value: ReadonlySignal<T[]>;
|
|
22
|
+
readonly valid: ReadonlySignal<boolean>;
|
|
23
|
+
readonly invalid: ReadonlySignal<boolean>;
|
|
24
|
+
readonly pending: ReadonlySignal<boolean>;
|
|
25
|
+
readonly disabled: ReadonlySignal<boolean>;
|
|
26
|
+
readonly touched: ReadonlySignal<boolean>;
|
|
27
|
+
readonly dirty: ReadonlySignal<boolean>;
|
|
28
|
+
readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
29
|
+
readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
30
|
+
private readonly rows;
|
|
31
|
+
/**
|
|
32
|
+
* Состав массива меняли методами ноды после создания или сброса. Как `dirty` поля: ставит запись
|
|
33
|
+
* через форму (`push`, `removeAt`, `move`, `setValue`…), а не запись в модель — загрузка данных
|
|
34
|
+
* массив изменённым не делает. Операция, которая состав не поменяла (`clear` пустого массива,
|
|
35
|
+
* `move(i, i)`), — тоже: поведение «очистить список при снятом флаге» после сброса формы её не
|
|
36
|
+
* испачкает. Снимает `reset`.
|
|
37
|
+
*/
|
|
38
|
+
private readonly reshaped;
|
|
39
|
+
/** Форма строки по фасаду под-модели элемента. */
|
|
40
|
+
private readonly cache;
|
|
41
|
+
/** Под-модели строк, для которых сейчас есть формы, — «прежний» набор для сверки. */
|
|
42
|
+
private mounted;
|
|
43
|
+
private readonly initial;
|
|
44
|
+
private readonly own;
|
|
45
|
+
private readonly availability;
|
|
46
|
+
/** Ожидание async-правила на ручке самого массива. */
|
|
47
|
+
private readonly ownPending;
|
|
48
|
+
private readonly stopSync;
|
|
49
|
+
/**
|
|
50
|
+
* @param control - Массив модели (`model.<путь>`).
|
|
51
|
+
* @param buildRow - Форма строки по её под-модели; инъекция снимает цикл импорта со сборкой.
|
|
52
|
+
*/
|
|
53
|
+
constructor(control: ModelArrayControl<T>, buildRow: (rowModel: FormModel<T>) => FormGroup<T>);
|
|
54
|
+
get length(): number;
|
|
55
|
+
at(index: number): FormGroup<T> | undefined;
|
|
56
|
+
map<R>(callback: (row: FormGroup<T>, index: number) => R): R[];
|
|
57
|
+
forEach(callback: (row: FormGroup<T>, index: number) => void): void;
|
|
58
|
+
keyAt(index: number): string;
|
|
59
|
+
push(item?: T): void;
|
|
60
|
+
insert(index: number, item?: T): void;
|
|
61
|
+
removeAt(index: number): void;
|
|
62
|
+
move(from: number, to: number): void;
|
|
63
|
+
swap(first: number, second: number): void;
|
|
64
|
+
clear(): void;
|
|
65
|
+
getValue(): T[];
|
|
66
|
+
setValue(values: T[]): void;
|
|
67
|
+
/**
|
|
68
|
+
* Сбросить массив: строки, замечания и `dirty`. Без аргумента — к точке отсчёта модели, той же, к
|
|
69
|
+
* которой возвращает `model.reset()`: элементам создания модели либо последнего
|
|
70
|
+
* `captureInitial()`.
|
|
71
|
+
*/
|
|
72
|
+
reset(values?: T[]): void;
|
|
73
|
+
/** Строки пересобираются из значений создания — их поведение считает заново. @internal */
|
|
74
|
+
resetToCreation(): void;
|
|
75
|
+
/** Точка отсчёта модели за массивом; без модели — элементы создания. */
|
|
76
|
+
private referenceItems;
|
|
77
|
+
markAsTouched(): void;
|
|
78
|
+
markAsUntouched(): void;
|
|
79
|
+
disable(): void;
|
|
80
|
+
/** Включить массив и строки; поля с блокировкой из схемы (`disabled: true`) остаются выключенными. */
|
|
81
|
+
enable(): void;
|
|
82
|
+
/** Каскад от контейнера — то же, что `enable()`: своей блокировки из схемы у массива нет. @internal */
|
|
83
|
+
enableFromParent(): void;
|
|
84
|
+
setIssues(issues: readonly ValidationIssue[]): void;
|
|
85
|
+
clearIssues(): void;
|
|
86
|
+
/** Своё ожидание async-правила на ручке массива; ожидание строк агрегат добавит сам. @internal */
|
|
87
|
+
setPending(pending: boolean): void;
|
|
88
|
+
/** Выполнить мутацию массива модели и отметить `dirty`, если состав строк изменился. */
|
|
89
|
+
private reshape;
|
|
90
|
+
/**
|
|
91
|
+
* Индекс модели для индекса строки формы. Без `null`-элементов они совпадают; с ними строка `i` —
|
|
92
|
+
* `i`-й непустой элемент, а индекс за последней строкой — конец массива модели. Читает модель, а не
|
|
93
|
+
* `mounted`: внутри `batch` синхронизация ещё не прошла.
|
|
94
|
+
*/
|
|
95
|
+
private modelIndex;
|
|
96
|
+
/** Под-модели строк по порядку — без подписки: мутацию могут вызвать из effect'а поведения. */
|
|
97
|
+
private composition;
|
|
98
|
+
/** Остановить синхронизацию и диспозить формы всех строк. @internal */
|
|
99
|
+
dispose(): void;
|
|
100
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { FieldConfig } from '../types/field-config';
|
|
3
|
+
import { ValidationIssue } from '../types/contracts';
|
|
4
|
+
import { ComponentRef } from '../types/schema-node';
|
|
5
|
+
import { FormControl } from '../types/nodes';
|
|
6
|
+
import { NODE_KIND, InternalFormNode } from './node-kind';
|
|
7
|
+
export declare class FormControlNode<T> implements FormControl<T>, InternalFormNode<T> {
|
|
8
|
+
readonly [NODE_KIND]: "control";
|
|
9
|
+
readonly value: ReadonlySignal<T>;
|
|
10
|
+
readonly valid: ReadonlySignal<boolean>;
|
|
11
|
+
readonly invalid: ReadonlySignal<boolean>;
|
|
12
|
+
readonly pending: ReadonlySignal<boolean>;
|
|
13
|
+
readonly disabled: ReadonlySignal<boolean>;
|
|
14
|
+
readonly touched: ReadonlySignal<boolean>;
|
|
15
|
+
/** Уходы из поля, включая повторные, — триггер стратегии `blur`. @internal */
|
|
16
|
+
readonly touches: ReadonlySignal<number>;
|
|
17
|
+
readonly dirty: ReadonlySignal<boolean>;
|
|
18
|
+
readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
19
|
+
readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
20
|
+
readonly component: ComponentRef | undefined;
|
|
21
|
+
readonly componentProps: ReadonlySignal<Readonly<Record<string, unknown>>>;
|
|
22
|
+
readonly shouldShowError: ReadonlySignal<boolean>;
|
|
23
|
+
readonly shouldShowIssues: ReadonlySignal<boolean>;
|
|
24
|
+
private readonly source;
|
|
25
|
+
/**
|
|
26
|
+
* Значение на момент построения. К нему возвращает `resetToCreation` (`resetOnDisable`), а
|
|
27
|
+
* {@link reset} — только поле над собственным сигналом, без модели за ним.
|
|
28
|
+
*/
|
|
29
|
+
private readonly initialValue;
|
|
30
|
+
private readonly interaction;
|
|
31
|
+
private readonly issueState;
|
|
32
|
+
private readonly availability;
|
|
33
|
+
/**
|
|
34
|
+
* Блокировка из схемы (`disabled: true` узла — вычисляемые `total`, `fullName`). Постоянная:
|
|
35
|
+
* `enable()` контейнера её не снимает, иначе «выключить форму на время отправки → включить»
|
|
36
|
+
* разблокировало бы такие поля. Снимает её только явный `enable()` этого поля.
|
|
37
|
+
*/
|
|
38
|
+
private locked;
|
|
39
|
+
private readonly pendingState;
|
|
40
|
+
private readonly componentState;
|
|
41
|
+
constructor(config: FieldConfig<T>);
|
|
42
|
+
getValue(): T;
|
|
43
|
+
setValue(value: T): void;
|
|
44
|
+
/**
|
|
45
|
+
* Сбросить поле: значение, замечания, `touched` и `dirty`. Без аргумента — к точке отсчёта модели,
|
|
46
|
+
* той же, к которой возвращает `model.reset()`: значению создания модели либо последнему
|
|
47
|
+
* `captureInitial()` (загруженные данные). Поле над собственным сигналом, без модели, — к значению,
|
|
48
|
+
* с которым нода создана.
|
|
49
|
+
*
|
|
50
|
+
* У производного поля (цель `compute`) без аргумента сбрасывается только состояние: значением
|
|
51
|
+
* владеет поведение, а значение создания ноды снято до первого расчёта — запись вернула бы
|
|
52
|
+
* «сырое» значение, которое `compute` пересчитает лишь при смене источников.
|
|
53
|
+
*/
|
|
54
|
+
reset(value?: T): void;
|
|
55
|
+
/** Точка отсчёта модели за полем; у поля без модели — значение создания. */
|
|
56
|
+
private referenceValue;
|
|
57
|
+
/** Значение создания и чистое состояние, у цели `compute` тоже — для `resetOnDisable`. @internal */
|
|
58
|
+
resetToCreation(): void;
|
|
59
|
+
private resetState;
|
|
60
|
+
markAsTouched(): void;
|
|
61
|
+
markAsUntouched(): void;
|
|
62
|
+
disable(): void;
|
|
63
|
+
enable(): void;
|
|
64
|
+
/** Каскад от контейнера: блокировку из схемы не снимает. @internal */
|
|
65
|
+
enableFromParent(): void;
|
|
66
|
+
setIssues(issues: readonly ValidationIssue[]): void;
|
|
67
|
+
clearIssues(): void;
|
|
68
|
+
updateComponentProps(patch: Record<string, unknown>): void;
|
|
69
|
+
/** Раннер валидации: начало (`true`) и конец (`false`) async-правил поля. @internal */
|
|
70
|
+
setPending(pending: boolean): void;
|
|
71
|
+
/** У поля подписок нет. @internal */
|
|
72
|
+
dispose(): void;
|
|
73
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ReadonlySignal, Signal } from '@preact/signals-core';
|
|
2
|
+
import { ValidationIssue } from '../types/contracts';
|
|
3
|
+
import { FormGroup } from '../types/nodes';
|
|
4
|
+
import { NODE_KIND, InternalFormNode } from './node-kind';
|
|
5
|
+
export declare class FormGroupNode<T> implements InternalFormNode<T> {
|
|
6
|
+
readonly [NODE_KIND]: "group";
|
|
7
|
+
readonly value: ReadonlySignal<T>;
|
|
8
|
+
readonly valid: ReadonlySignal<boolean>;
|
|
9
|
+
readonly invalid: ReadonlySignal<boolean>;
|
|
10
|
+
readonly pending: ReadonlySignal<boolean>;
|
|
11
|
+
readonly disabled: ReadonlySignal<boolean>;
|
|
12
|
+
readonly touched: ReadonlySignal<boolean>;
|
|
13
|
+
readonly dirty: ReadonlySignal<boolean>;
|
|
14
|
+
readonly issues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
15
|
+
readonly ownIssues: ReadonlySignal<readonly ValidationIssue[]>;
|
|
16
|
+
/** Сырые ноды детей по именам полей данных. */
|
|
17
|
+
private readonly children;
|
|
18
|
+
/** Замечания самой группы (ответ сервера на форму целиком). */
|
|
19
|
+
private readonly own;
|
|
20
|
+
private readonly availability;
|
|
21
|
+
/** Ожидание async-правила на ручке самой группы. */
|
|
22
|
+
private readonly ownPending;
|
|
23
|
+
/**
|
|
24
|
+
* Лист → его записываемый сигнал модели: по нему `setValue` узнаёт цели `compute` (их
|
|
25
|
+
* помечает `markDerived`), а не по computed-обёртке `value` ноды.
|
|
26
|
+
*/
|
|
27
|
+
private readonly leafSignals;
|
|
28
|
+
private behaviorCleanup;
|
|
29
|
+
private face;
|
|
30
|
+
/**
|
|
31
|
+
* @param children - Готовые ноды детей по именам полей данных. Вид ребёнка задан нодой: по
|
|
32
|
+
* именам и значениям группа ничего не угадывает.
|
|
33
|
+
*/
|
|
34
|
+
constructor(children: Iterable<readonly [string, InternalFormNode]>);
|
|
35
|
+
/** Лицо группы: прокси с нодами полей. Строится один раз. */
|
|
36
|
+
get proxy(): FormGroup<T>;
|
|
37
|
+
getValue(): T;
|
|
38
|
+
/** Записать значения полей; цели `compute` пропускаются — ими владеет поведение. */
|
|
39
|
+
setValue(value: T): void;
|
|
40
|
+
/**
|
|
41
|
+
* Сбросить группу: замечания группы и всех полей, `touched`, `dirty` и значения. Без аргумента —
|
|
42
|
+
* к точке отсчёта модели, той же, к которой возвращает `model.reset()` (значения создания модели
|
|
43
|
+
* либо последнего `captureInitial()`), — и форма после сброса не изменена.
|
|
44
|
+
* Цели `compute` значения не получают, как и в `setValue`: ими владеет поведение.
|
|
45
|
+
*/
|
|
46
|
+
reset(value?: T): void;
|
|
47
|
+
/** Сброс всех полей группы к значениям создания, у целей `compute` тоже. @internal */
|
|
48
|
+
resetToCreation(): void;
|
|
49
|
+
markAsTouched(): void;
|
|
50
|
+
markAsUntouched(): void;
|
|
51
|
+
disable(): void;
|
|
52
|
+
/** Включить группу и детей; поля с блокировкой из схемы (`disabled: true`) остаются выключенными. */
|
|
53
|
+
enable(): void;
|
|
54
|
+
/** Каскад от контейнера — то же, что `enable()`: своей блокировки из схемы у группы нет. @internal */
|
|
55
|
+
enableFromParent(): void;
|
|
56
|
+
setIssues(issues: readonly ValidationIssue[]): void;
|
|
57
|
+
clearIssues(): void;
|
|
58
|
+
/** Своё ожидание async-правила на ручке группы; ожидание детей агрегат добавит сам. @internal */
|
|
59
|
+
setPending(pending: boolean): void;
|
|
60
|
+
/** Прикрепить cleanup поведения формы (`createForm({ behavior })`). @internal */
|
|
61
|
+
attachBehaviorCleanup(cleanup: () => void): void;
|
|
62
|
+
/** Связать лист с его сигналом модели для `setValue`. @internal */
|
|
63
|
+
registerLeafSignal(node: InternalFormNode, source: Signal<unknown>): void;
|
|
64
|
+
/** Снять поведение и подписки всех детей. @internal */
|
|
65
|
+
dispose(): void;
|
|
66
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FormNode } from '../types/nodes';
|
|
2
|
+
export declare const NODE_KIND: unique symbol;
|
|
3
|
+
/** Сырой узел за прокси группы: внутренним вызовам нужен он, а не лицо с полями. */
|
|
4
|
+
export declare const RAW_NODE: unique symbol;
|
|
5
|
+
export type NodeKind = 'control' | 'group' | 'array';
|
|
6
|
+
/** Внутренние члены ноды: их зовут сборка, раннер валидации и массив строк. */
|
|
7
|
+
export interface InternalFormNode<T = unknown> extends FormNode<T> {
|
|
8
|
+
readonly [NODE_KIND]: NodeKind;
|
|
9
|
+
/** Раннер валидации: начало и конец async-правил; у группы и массива — ничего. */
|
|
10
|
+
setPending(pending: boolean): void;
|
|
11
|
+
/** Снять подписки ноды и её детей. */
|
|
12
|
+
dispose(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Сброс для `resetOnDisable`: вернуть значения создания и очистить состояние — в том числе у
|
|
15
|
+
* целей `compute`. Выключенное поле не должно нести прежний расчёт (его `compute` обязан нести то
|
|
16
|
+
* же условие `when`); публичный `reset()` без аргумента производное поле не пишет.
|
|
17
|
+
*/
|
|
18
|
+
resetToCreation(): void;
|
|
19
|
+
/**
|
|
20
|
+
* Включение каскадом от контейнера (`enable()` группы или массива). Отличается от `enable()` одним:
|
|
21
|
+
* блокировку из схемы (`disabled: true` узла) не снимает — её снимает только явный `enable()`
|
|
22
|
+
* самого поля.
|
|
23
|
+
*/
|
|
24
|
+
enableFromParent(): void;
|
|
25
|
+
}
|
|
26
|
+
/** Вид ноды либо `undefined`, если значение — не нода формы. */
|
|
27
|
+
export declare function kindOf(value: unknown): NodeKind | undefined;
|
|
28
|
+
/** Сырой узел: за прокси группы — её класс, у остальных — сама нода. */
|
|
29
|
+
export declare function rawNode<N extends object>(node: N): InternalFormNode;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
export interface AvailabilityState {
|
|
3
|
+
readonly disabled: ReadonlySignal<boolean>;
|
|
4
|
+
setDisabled(disabled: boolean): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function createAvailability(initiallyDisabled?: boolean): AvailabilityState;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
export interface ComponentState {
|
|
3
|
+
readonly props: ReadonlySignal<Readonly<Record<string, unknown>>>;
|
|
4
|
+
update(patch: Record<string, unknown>): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function createComponentState(initial?: Record<string, unknown>): ComponentState;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модули состояния ноды формы. Каждый держит одну сторону состояния и ничего не знает о других;
|
|
3
|
+
* производные (`valid`, `invalid`, `pending`) складывает нода.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
* @module form/nodes/state
|
|
7
|
+
*/
|
|
8
|
+
export { createInteraction } from './interaction';
|
|
9
|
+
export { createIssues } from './issues';
|
|
10
|
+
export { createAvailability } from './availability';
|
|
11
|
+
export { createPending } from './pending';
|
|
12
|
+
export { createComponentState } from './component';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
export interface InteractionState {
|
|
3
|
+
readonly touched: ReadonlySignal<boolean>;
|
|
4
|
+
readonly dirty: ReadonlySignal<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Сколько раз поле помечали тронутым — каждый уход из поля, и повторный тоже. У тронутого поля
|
|
7
|
+
* `touched` при новом уходе не меняется; по счётчику стратегия `blur` видит и его.
|
|
8
|
+
*/
|
|
9
|
+
readonly touches: ReadonlySignal<number>;
|
|
10
|
+
setTouched(touched: boolean): void;
|
|
11
|
+
setDirty(dirty: boolean): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function createInteraction(): InteractionState;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { ValidationIssue } from '../../types/contracts';
|
|
3
|
+
export interface IssuesState {
|
|
4
|
+
readonly list: ReadonlySignal<readonly ValidationIssue[]>;
|
|
5
|
+
readonly blocking: ReadonlySignal<boolean>;
|
|
6
|
+
set(issues: readonly ValidationIssue[]): void;
|
|
7
|
+
clear(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function createIssues(): IssuesState;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FormSchemaNode } from '../types/schema-node';
|
|
2
|
+
import { CompiledNode, CompiledSchema, SchemaScopeId } from './compiled';
|
|
3
|
+
/**
|
|
4
|
+
* Узел скомпилированной схемы, а не авторский узел?
|
|
5
|
+
*
|
|
6
|
+
* @param value - Что угодно.
|
|
7
|
+
* @returns `true` для узла, выданного {@link compileSchema}.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* isCompiledNode(compileSchema(tree, model).root); // true
|
|
12
|
+
* isCompiledNode(tree); // false
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @group Schema
|
|
16
|
+
*/
|
|
17
|
+
export declare function isCompiledNode(value: unknown): value is CompiledNode;
|
|
18
|
+
/** Опции {@link compileSchema}. */
|
|
19
|
+
export interface CompileSchemaOptions {
|
|
20
|
+
/** Идентификатор области дерева; по умолчанию корень `#`. */
|
|
21
|
+
readonly scope?: SchemaScopeId;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Скомпилировать дерево схемы: вид каждого узла, нормализованные привязки, идентификаторы и
|
|
25
|
+
* индексы области.
|
|
26
|
+
*
|
|
27
|
+
* Повтор `selector` в одной области — исключение с путями обоих узлов. Неизвестный ключ узла и
|
|
28
|
+
* нераспознанная привязка — предупреждения в dev; такой узел становится контейнером. Вторая
|
|
29
|
+
* привязка той же ручки — тоже предупреждение: нода формы у ручки одна.
|
|
30
|
+
*
|
|
31
|
+
* @param tree - Корень авторского дерева ({@link FormSchemaNode}).
|
|
32
|
+
* @param model - Модель области (корень сборки, под-модель строки либо группы): пути вложенных
|
|
33
|
+
* областей считаются от неё; без неё — абсолютные пути ручек (`#/phones.1.address`).
|
|
34
|
+
* @param options - {@link CompileSchemaOptions}.
|
|
35
|
+
* @returns {@link CompiledSchema} области.
|
|
36
|
+
* @throws Error при повторе `selector` в области, при селекторе, совпавшем с позиционным путём
|
|
37
|
+
* узла (`#/1`), и при зацикленном дереве (в том числе через подформу).
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const model = createModel({ email: '', phones: [{ number: '' }] });
|
|
42
|
+
* const compiled = compileSchema(
|
|
43
|
+
* {
|
|
44
|
+
* children: [
|
|
45
|
+
* { selector: 'email', model: model.$.email, component: Input },
|
|
46
|
+
* { model: model.$.phones, item: (row) => ({ model: row.$.number, component: Input }) },
|
|
47
|
+
* ],
|
|
48
|
+
* },
|
|
49
|
+
* model
|
|
50
|
+
* );
|
|
51
|
+
*
|
|
52
|
+
* compiled.nodes.get('email' as SchemaNodeId)?.kind; // 'field'
|
|
53
|
+
* compiled.fields.get(model.$.email)?.component; // Input
|
|
54
|
+
* compiled.arrays.get(model.$.phones)?.row(model.phones.at(0)!).scope; // '#/phones[0]'
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @group Schema
|
|
58
|
+
*/
|
|
59
|
+
export declare function compileSchema(tree: FormSchemaNode, model?: object, options?: CompileSchemaOptions): CompiledSchema;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Signal } from '@preact/signals-core';
|
|
2
|
+
import { ComponentRef, FormSchemaNode, SchemaArrayControl, SchemaArrayHandle, SchemaGroupHandle, SchemaValueHandle } from '../types/schema-node';
|
|
3
|
+
/**
|
|
4
|
+
* Идентификатор узла: `selector` автора, а у узла без него — позиционный путь от корня области
|
|
5
|
+
* (`#/0/2`). Уникален в пределах области.
|
|
6
|
+
*
|
|
7
|
+
* @group Schema
|
|
8
|
+
*/
|
|
9
|
+
export type SchemaNodeId = string & {
|
|
10
|
+
readonly __nodeId: true;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Идентификатор области схемы: `#` — корень, `#/address` — подформа, `#/phones[3]` — строка
|
|
14
|
+
* массива (в скобках — стабильный ключ строки, который не меняется при перестановке).
|
|
15
|
+
*
|
|
16
|
+
* @group Schema
|
|
17
|
+
*/
|
|
18
|
+
export type SchemaScopeId = string & {
|
|
19
|
+
readonly __scopeId: true;
|
|
20
|
+
};
|
|
21
|
+
/** Ключи, общие для узлов всех видов, кроме текста. */
|
|
22
|
+
interface CompiledNodeBase {
|
|
23
|
+
/** Идентификатор в области: `selector`, иначе позиционный путь. */
|
|
24
|
+
readonly id: SchemaNodeId;
|
|
25
|
+
/** Позиционный путь узла в области (`#/0/2`) — для сообщений об ошибках. */
|
|
26
|
+
readonly path: string;
|
|
27
|
+
/** `selector` автора: по нему узел адресуют поведение и шаги. */
|
|
28
|
+
readonly selector: string | undefined;
|
|
29
|
+
/** Область, которой принадлежит узел. */
|
|
30
|
+
readonly scope: SchemaScopeId;
|
|
31
|
+
/** Компонент узла как есть: ядро его не интерпретирует. */
|
|
32
|
+
readonly component: ComponentRef | undefined;
|
|
33
|
+
/** `componentProps` автора; без них — пустой объект. */
|
|
34
|
+
readonly props: Readonly<Record<string, unknown>>;
|
|
35
|
+
/** Авторский узел, из которого скомпилирован этот. */
|
|
36
|
+
readonly source: FormSchemaNode;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Поле: узел, привязанный к ручке значения — листу или массиву целиком.
|
|
40
|
+
*
|
|
41
|
+
* @group Schema
|
|
42
|
+
*/
|
|
43
|
+
export interface CompiledFieldNode extends CompiledNodeBase {
|
|
44
|
+
readonly kind: 'field';
|
|
45
|
+
/** Ручка значения `model.$.<лист | массив>`. */
|
|
46
|
+
readonly handle: SchemaValueHandle;
|
|
47
|
+
/** Поле создаётся отключённым. */
|
|
48
|
+
readonly disabled: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Массив под-форм: у каждой строки своя форма и своя область схемы.
|
|
52
|
+
*
|
|
53
|
+
* @group Schema
|
|
54
|
+
*/
|
|
55
|
+
export interface CompiledArrayNode extends CompiledNodeBase {
|
|
56
|
+
readonly kind: 'array';
|
|
57
|
+
/** Ручка массива `model.$.<массив>` — привязка фасадом нормализуется к ней. */
|
|
58
|
+
readonly handle: SchemaArrayHandle;
|
|
59
|
+
/** Фасад массива модели: длина, строки и мутации. */
|
|
60
|
+
readonly control: SchemaArrayControl;
|
|
61
|
+
/** Запасной шаблон нового элемента из узла; шаблон модели (`arrayOf`) главнее. */
|
|
62
|
+
readonly initialValue: unknown;
|
|
63
|
+
/**
|
|
64
|
+
* Скомпилированная схема строки — одна на под-модель строки: сборка формы строки и рендерер
|
|
65
|
+
* получают один и тот же объект.
|
|
66
|
+
*/
|
|
67
|
+
row(rowModel: object): CompiledSchema;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Подформа: часть схемы, подключённая к группе модели. Содержимое компилируется сразу, в своей
|
|
71
|
+
* области.
|
|
72
|
+
*
|
|
73
|
+
* @group Schema
|
|
74
|
+
*/
|
|
75
|
+
export interface CompiledPartNode extends CompiledNodeBase {
|
|
76
|
+
readonly kind: 'part';
|
|
77
|
+
/** Ручка группы `model.$.<группа>`. */
|
|
78
|
+
readonly handle: SchemaGroupHandle;
|
|
79
|
+
/** Под-модель группы — ключ области подформы. */
|
|
80
|
+
readonly model: object;
|
|
81
|
+
/** Скомпилированное содержимое части. */
|
|
82
|
+
readonly content: CompiledSchema;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Контейнер: узел с детьми либо без привязки — секция, шаг визарда, html-тег. Без `component`
|
|
86
|
+
* рендерер рисует только детей.
|
|
87
|
+
*
|
|
88
|
+
* @group Schema
|
|
89
|
+
*/
|
|
90
|
+
export interface CompiledContainerNode extends CompiledNodeBase {
|
|
91
|
+
readonly kind: 'container';
|
|
92
|
+
/** Дети в порядке следования: узлы и текст. */
|
|
93
|
+
readonly children: readonly CompiledChild[];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Часть текста: литерал, число или сигнал (его значение выводится и обновляется на месте).
|
|
97
|
+
*
|
|
98
|
+
* @group Schema
|
|
99
|
+
*/
|
|
100
|
+
export type SchemaTextPart = string | number | Signal<any>;
|
|
101
|
+
/**
|
|
102
|
+
* Текст контейнера: подряд идущие текстовые дети автора, склеенные в один узел.
|
|
103
|
+
*
|
|
104
|
+
* @group Schema
|
|
105
|
+
*/
|
|
106
|
+
export interface CompiledTextNode {
|
|
107
|
+
readonly kind: 'text';
|
|
108
|
+
/** Позиционный путь первой части. */
|
|
109
|
+
readonly id: SchemaNodeId;
|
|
110
|
+
readonly parts: readonly SchemaTextPart[];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Узел скомпилированной схемы: поле, массив под-форм, подформа или контейнер.
|
|
114
|
+
*
|
|
115
|
+
* @group Schema
|
|
116
|
+
*/
|
|
117
|
+
export type CompiledNode = CompiledFieldNode | CompiledArrayNode | CompiledPartNode | CompiledContainerNode;
|
|
118
|
+
/**
|
|
119
|
+
* Ребёнок контейнера: узел либо текст.
|
|
120
|
+
*
|
|
121
|
+
* @group Schema
|
|
122
|
+
*/
|
|
123
|
+
export type CompiledChild = CompiledNode | CompiledTextNode;
|
|
124
|
+
/**
|
|
125
|
+
* Скомпилированная схема одной области: корня сборки, строки массива или подформы.
|
|
126
|
+
*
|
|
127
|
+
* @group Schema
|
|
128
|
+
*/
|
|
129
|
+
export interface CompiledSchema {
|
|
130
|
+
/** Идентификатор области. */
|
|
131
|
+
readonly scope: SchemaScopeId;
|
|
132
|
+
/** Модель области: корень сборки, под-модель строки или группы; без модели — `undefined`. */
|
|
133
|
+
readonly model: object | undefined;
|
|
134
|
+
/** Корневой узел области. */
|
|
135
|
+
readonly root: CompiledNode;
|
|
136
|
+
/** Узлы этой области по идентификатору; узлы строк и подформ лежат в их схемах. */
|
|
137
|
+
readonly nodes: ReadonlyMap<SchemaNodeId, CompiledNode>;
|
|
138
|
+
/** Поля по ручке модели — этой области и её подформ (строки массивов — в их схемах). */
|
|
139
|
+
readonly fields: ReadonlyMap<object, CompiledFieldNode>;
|
|
140
|
+
/** Массивы под-форм по ручке модели — этой области и её подформ. */
|
|
141
|
+
readonly arrays: ReadonlyMap<object, CompiledArrayNode>;
|
|
142
|
+
}
|
|
143
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Стабильный ключ строки массива под-форм — по идентичности под-модели строки.
|
|
3
|
+
*
|
|
4
|
+
* Один источник для области строки в скомпилированной схеме (`#/phones[<ключ>]`) и для
|
|
5
|
+
* `FormArray.keyAt(index)`: ключ держится за элемент модели и не меняется при `move` и `swap`.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
* @module form/schema/row-key
|
|
9
|
+
*/
|
|
10
|
+
/** Ключ строки по её под-модели; первый запрос выдаёт новый ключ. */
|
|
11
|
+
export declare function rowKeyOf(rowModel: object): number;
|