@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
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { Signal, ReadonlySignal } from '@preact/signals-core';
|
|
2
|
-
import { FormNode } from './form-node';
|
|
3
|
-
import { FieldStatus, ValidationError } from '../types/index';
|
|
4
|
-
import { FormModel } from '../../model/types';
|
|
5
|
-
import { FormProxy } from '../types/form-proxy';
|
|
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(a: number, b: number): void;
|
|
15
|
-
clear(): void;
|
|
16
|
-
toArray(): TItem[];
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Узел массива, делегирующий данные массиву {@link FormModel} (архитектура M1).
|
|
20
|
-
*
|
|
21
|
-
* В отличие от {@link ArrayNode} (владеет элементами сам), `ModelArrayNode` НЕ владеет данными:
|
|
22
|
-
* массив принадлежит модели, а узел держит per-item формы элементов (привязанные к сигналам
|
|
23
|
-
* под-моделей) и синхронизирует их с длиной массива модели. Мутации (`push`/`removeAt`/`move`/…)
|
|
24
|
-
* делегируются массиву модели; per-item формы кэшируются по идентичности под-модели, поэтому при
|
|
25
|
-
* reorder/повторном рендере не пересоздаются (состояние и валидация сохраняются). Реализует тот же
|
|
26
|
-
* контракт, что ждут секции массива и `useFormControl` (`length`/`value`/`valid`/`errors`/`at`/`push`/…).
|
|
27
|
-
*
|
|
28
|
-
* Обычно создаётся не напрямую, а `createForm({ model, schema })`: когда в схеме встречается узел
|
|
29
|
-
* массива `{ array: model.<field>, item: (item) => itemSchema }`, форма материализует его как
|
|
30
|
-
* `ModelArrayNode` и кладёт под `form.<field>` (совместим с `FormArraySection`).
|
|
31
|
-
*
|
|
32
|
-
* @group Nodes
|
|
33
|
-
*
|
|
34
|
-
* @example Массив как часть формы (через createForm)
|
|
35
|
-
* ```typescript
|
|
36
|
-
* const model = createModel<{ rows: { name: string; qty: number }[] }>({ rows: [] });
|
|
37
|
-
*
|
|
38
|
-
* const rowItem = (item: FormModel<{ name: string; qty: number }>) => ({
|
|
39
|
-
* name: { value: item.$.name, component: Input },
|
|
40
|
-
* qty: { value: item.$.qty, component: Input },
|
|
41
|
-
* });
|
|
42
|
-
*
|
|
43
|
-
* const form = createForm({
|
|
44
|
-
* model,
|
|
45
|
-
* schema: {
|
|
46
|
-
* children: [{ array: model.rows, item: rowItem }],
|
|
47
|
-
* },
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* const rows = form.rows as unknown as ModelArrayNode<{ name: string; qty: number }>;
|
|
51
|
-
* rows.push({ name: 'A', qty: 1 }); // мутация уезжает в model.rows
|
|
52
|
-
* rows.at(0)?.name.setValue('B'); // правка поля элемента доезжает в под-модель
|
|
53
|
-
* rows.length.value; // 1 (реактивная длина)
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
export declare class ModelArrayNode<T extends object> extends FormNode<T[]> {
|
|
57
|
-
private readonly control;
|
|
58
|
-
private readonly buildItem;
|
|
59
|
-
private readonly itemNodes;
|
|
60
|
-
private readonly cache;
|
|
61
|
-
private mountedModels;
|
|
62
|
-
private readonly initial;
|
|
63
|
-
private readonly _arrayErrors;
|
|
64
|
-
private readonly disposeSync;
|
|
65
|
-
/**
|
|
66
|
-
* Сигнал per-item форм. Публичен в т.ч. чтобы `isArrayNode` распознавал узел как массив
|
|
67
|
-
* (duck-typing проверяет наличие `items`).
|
|
68
|
-
*/
|
|
69
|
-
get items(): Signal<FormProxy<T>[]>;
|
|
70
|
-
readonly value: ReadonlySignal<T[]>;
|
|
71
|
-
readonly valid: ReadonlySignal<boolean>;
|
|
72
|
-
readonly invalid: ReadonlySignal<boolean>;
|
|
73
|
-
readonly pending: ReadonlySignal<boolean>;
|
|
74
|
-
readonly touched: ReadonlySignal<boolean>;
|
|
75
|
-
readonly dirty: ReadonlySignal<boolean>;
|
|
76
|
-
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
77
|
-
readonly status: ReadonlySignal<FieldStatus>;
|
|
78
|
-
readonly length: ReadonlySignal<number>;
|
|
79
|
-
/**
|
|
80
|
-
* @param control Реактивный массив модели (`model.<path>`).
|
|
81
|
-
* @param buildItem Строитель формы элемента по его под-модели (инъекция, чтобы избежать цикла
|
|
82
|
-
* импорта с `create-form`). Обычно `(item) => createForm({ model: item, schema: itemSchema(item) })`.
|
|
83
|
-
*/
|
|
84
|
-
constructor(control: ModelArrayControl<T>, buildItem: (item: FormModel<T>) => FormProxy<T>);
|
|
85
|
-
at(index: number): FormProxy<T> | undefined;
|
|
86
|
-
map<R>(fn: (item: FormProxy<T>, index: number) => R): R[];
|
|
87
|
-
forEach(fn: (item: FormProxy<T>, index: number) => void): void;
|
|
88
|
-
push(item?: Partial<T>): void;
|
|
89
|
-
insert(index: number, item?: Partial<T>): void;
|
|
90
|
-
removeAt(index: number): void;
|
|
91
|
-
/**
|
|
92
|
-
* Переместить элемент модель-массива. Делегирует мутацию массиву модели; внутренний `effect`
|
|
93
|
-
* пересоберёт `itemNodes` в новом порядке (per-item формы сохраняются по кешу идентичности).
|
|
94
|
-
*/
|
|
95
|
-
move(from: number, to: number): void;
|
|
96
|
-
/** Поменять местами два элемента модель-массива (см. {@link move}). */
|
|
97
|
-
swap(a: number, b: number): void;
|
|
98
|
-
clear(): void;
|
|
99
|
-
getValue(): T[];
|
|
100
|
-
setValue(values: T[]): void;
|
|
101
|
-
patchValue(values: Partial<T[]>): void;
|
|
102
|
-
reset(values?: T[]): void;
|
|
103
|
-
resetToInitial(): void;
|
|
104
|
-
validate(): Promise<boolean>;
|
|
105
|
-
setErrors(errors: ValidationError[]): void;
|
|
106
|
-
clearErrors(): void;
|
|
107
|
-
protected onMarkAsTouched(): void;
|
|
108
|
-
protected onMarkAsUntouched(): void;
|
|
109
|
-
protected onMarkAsDirty(): void;
|
|
110
|
-
protected onMarkAsPristine(): void;
|
|
111
|
-
protected onDisable(): void;
|
|
112
|
-
protected onEnable(): void;
|
|
113
|
-
/** Диспозит форму элемента (эффекты/поведения/вложенные подписки); терпима к не-GroupNode. */
|
|
114
|
-
private disposeItemNode;
|
|
115
|
-
/**
|
|
116
|
-
* Очистка подписки синхронизации И детерминированный teardown всех форм элементов.
|
|
117
|
-
* Без пробрасывания dispose в itemNodes эффекты/поведения каждой формы элемента (в т.ч. disposeSync
|
|
118
|
-
* вложенных ModelArrayNode) остались бы жить после teardown формы — утечка подписок.
|
|
119
|
-
*/
|
|
120
|
-
dispose(): void;
|
|
121
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Менеджер подписок для FormNode
|
|
3
|
-
*
|
|
4
|
-
* Централизует управление effect-подписками в узлах формы,
|
|
5
|
-
* предотвращает утечки памяти и упрощает отладку.
|
|
6
|
-
*
|
|
7
|
-
* Каждая подписка имеет уникальный ключ, что позволяет:
|
|
8
|
-
* - Отписываться от конкретной подписки по ключу
|
|
9
|
-
* - Автоматически заменять существующие подписки
|
|
10
|
-
* - Отслеживать количество активных подписок (для отладки)
|
|
11
|
-
*
|
|
12
|
-
* @internal
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* class FieldNode {
|
|
17
|
-
* private subscriptions = new SubscriptionManager();
|
|
18
|
-
*
|
|
19
|
-
* watch(callback: Function) {
|
|
20
|
-
* const dispose = effect(() => callback(this.value.value));
|
|
21
|
-
* return this.subscriptions.add('watch', dispose);
|
|
22
|
-
* }
|
|
23
|
-
*
|
|
24
|
-
* dispose() {
|
|
25
|
-
* this.subscriptions.clear();
|
|
26
|
-
* }
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export declare class SubscriptionManager {
|
|
31
|
-
/**
|
|
32
|
-
* Хранилище подписок
|
|
33
|
-
* Ключ: уникальный идентификатор подписки
|
|
34
|
-
* Значение: функция отписки (dispose)
|
|
35
|
-
*/
|
|
36
|
-
private subscriptions;
|
|
37
|
-
/**
|
|
38
|
-
* Добавляет подписку
|
|
39
|
-
*
|
|
40
|
-
* Если подписка с таким ключом уже существует, отписывается от неё
|
|
41
|
-
* и заменяет новой. Это предотвращает утечки памяти при повторной
|
|
42
|
-
* регистрации подписки с тем же ключом.
|
|
43
|
-
*
|
|
44
|
-
* @param key Уникальный ключ подписки
|
|
45
|
-
* @param dispose Функция отписки (обычно возвращаемая из effect())
|
|
46
|
-
* @returns Функция для отписки от этой конкретной подписки
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```typescript
|
|
50
|
-
* const manager = new SubscriptionManager();
|
|
51
|
-
*
|
|
52
|
-
* // Добавление подписки
|
|
53
|
-
* const unsubscribe = manager.add('mySubscription', () => {
|
|
54
|
-
* console.log('Disposing subscription');
|
|
55
|
-
* });
|
|
56
|
-
*
|
|
57
|
-
* // Отписка через возвращаемую функцию
|
|
58
|
-
* unsubscribe();
|
|
59
|
-
*
|
|
60
|
-
* // Или через manager.remove()
|
|
61
|
-
* manager.add('anotherSub', disposeFn);
|
|
62
|
-
* manager.remove('anotherSub');
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
add(key: string, dispose: () => void): () => void;
|
|
66
|
-
/**
|
|
67
|
-
* Удаляет подписку по ключу
|
|
68
|
-
*
|
|
69
|
-
* Вызывает функцию отписки и удаляет подписку из хранилища.
|
|
70
|
-
* Если подписка с таким ключом не найдена, ничего не делает.
|
|
71
|
-
*
|
|
72
|
-
* @param key Ключ подписки
|
|
73
|
-
* @returns true, если подписка была удалена, false если не найдена
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```typescript
|
|
77
|
-
* const manager = new SubscriptionManager();
|
|
78
|
-
* manager.add('mySub', disposeFn);
|
|
79
|
-
*
|
|
80
|
-
* // Удаление подписки
|
|
81
|
-
* const removed = manager.remove('mySub'); // true
|
|
82
|
-
*
|
|
83
|
-
* // Попытка удалить несуществующую подписку
|
|
84
|
-
* const removed2 = manager.remove('nonExistent'); // false
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
remove(key: string): boolean;
|
|
88
|
-
/**
|
|
89
|
-
* Очищает все подписки
|
|
90
|
-
*
|
|
91
|
-
* Вызывает функции отписки для всех активных подписок
|
|
92
|
-
* и очищает хранилище. Обычно используется при dispose узла формы.
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* ```typescript
|
|
96
|
-
* class FieldNode {
|
|
97
|
-
* private subscriptions = new SubscriptionManager();
|
|
98
|
-
*
|
|
99
|
-
* dispose() {
|
|
100
|
-
* // Отписываемся от всех effect
|
|
101
|
-
* this.subscriptions.clear();
|
|
102
|
-
* }
|
|
103
|
-
* }
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
clear(): void;
|
|
107
|
-
/**
|
|
108
|
-
* Возвращает количество активных подписок
|
|
109
|
-
*
|
|
110
|
-
* Полезно для отладки утечек памяти. Если количество подписок
|
|
111
|
-
* растет без ограничений, это может указывать на то, что
|
|
112
|
-
* компоненты не отписываются должным образом.
|
|
113
|
-
*
|
|
114
|
-
* @returns Количество активных подписок
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* ```typescript
|
|
118
|
-
* const manager = new SubscriptionManager();
|
|
119
|
-
* console.log(manager.size()); // 0
|
|
120
|
-
*
|
|
121
|
-
* manager.add('sub1', disposeFn1);
|
|
122
|
-
* manager.add('sub2', disposeFn2);
|
|
123
|
-
* console.log(manager.size()); // 2
|
|
124
|
-
*
|
|
125
|
-
* manager.clear();
|
|
126
|
-
* console.log(manager.size()); // 0
|
|
127
|
-
* ```
|
|
128
|
-
*/
|
|
129
|
-
size(): number;
|
|
130
|
-
/**
|
|
131
|
-
* Проверяет, есть ли подписка с данным ключом
|
|
132
|
-
*
|
|
133
|
-
* @param key Ключ подписки
|
|
134
|
-
* @returns true, если подписка существует
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```typescript
|
|
138
|
-
* const manager = new SubscriptionManager();
|
|
139
|
-
* manager.add('mySub', disposeFn);
|
|
140
|
-
*
|
|
141
|
-
* console.log(manager.has('mySub')); // true
|
|
142
|
-
* console.log(manager.has('nonExistent')); // false
|
|
143
|
-
* ```
|
|
144
|
-
*/
|
|
145
|
-
has(key: string): boolean;
|
|
146
|
-
/**
|
|
147
|
-
* Возвращает список всех ключей активных подписок
|
|
148
|
-
*
|
|
149
|
-
* Полезно для отладки: можно увидеть, какие подписки активны.
|
|
150
|
-
*
|
|
151
|
-
* @returns Массив ключей всех активных подписок
|
|
152
|
-
*
|
|
153
|
-
* @example
|
|
154
|
-
* ```typescript
|
|
155
|
-
* const manager = new SubscriptionManager();
|
|
156
|
-
* manager.add('watch-value', disposeFn1);
|
|
157
|
-
* manager.add('watch-errors', disposeFn2);
|
|
158
|
-
*
|
|
159
|
-
* console.log(manager.getKeys()); // ['watch-value', 'watch-errors']
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
getKeys(): string[];
|
|
163
|
-
/**
|
|
164
|
-
* Отписывается от всех подписок (алиас для clear())
|
|
165
|
-
*
|
|
166
|
-
* Используется при dispose() узла формы для совместимости с ожидаемым API.
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```typescript
|
|
170
|
-
* class FieldNode {
|
|
171
|
-
* private subscriptions = new SubscriptionManager();
|
|
172
|
-
*
|
|
173
|
-
* dispose() {
|
|
174
|
-
* this.subscriptions.dispose();
|
|
175
|
-
* }
|
|
176
|
-
* }
|
|
177
|
-
* ```
|
|
178
|
-
*/
|
|
179
|
-
dispose(): void;
|
|
180
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
-
import { FieldStatus } from './types/index';
|
|
3
|
-
/**
|
|
4
|
-
* FormStatusMachine - управляет состоянием поля формы
|
|
5
|
-
*
|
|
6
|
-
* Предоставляет:
|
|
7
|
-
* - Единый источник истины для статуса
|
|
8
|
-
* - Computed signals для derived состояний (valid, invalid, pending, disabled)
|
|
9
|
-
* - Валидацию переходов между состояниями
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* const statusMachine = new FormStatusMachine('valid');
|
|
14
|
-
*
|
|
15
|
-
* // Начало валидации
|
|
16
|
-
* statusMachine.startValidation();
|
|
17
|
-
* console.log(statusMachine.pending.value); // true
|
|
18
|
-
*
|
|
19
|
-
* // Завершение валидации с ошибками
|
|
20
|
-
* statusMachine.completeValidation(true);
|
|
21
|
-
* console.log(statusMachine.invalid.value); // true
|
|
22
|
-
*
|
|
23
|
-
* // Отключение поля
|
|
24
|
-
* statusMachine.disable();
|
|
25
|
-
* console.log(statusMachine.disabled.value); // true
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export declare class FormStatusMachine {
|
|
29
|
-
/** Внутренний сигнал статуса */
|
|
30
|
-
private readonly _status;
|
|
31
|
-
/** Публичный read-only сигнал статуса */
|
|
32
|
-
readonly status: ReadonlySignal<FieldStatus>;
|
|
33
|
-
/** Поле валидно */
|
|
34
|
-
readonly valid: ReadonlySignal<boolean>;
|
|
35
|
-
/** Поле невалидно */
|
|
36
|
-
readonly invalid: ReadonlySignal<boolean>;
|
|
37
|
-
/** Идет валидация */
|
|
38
|
-
readonly pending: ReadonlySignal<boolean>;
|
|
39
|
-
/** Поле отключено */
|
|
40
|
-
readonly disabled: ReadonlySignal<boolean>;
|
|
41
|
-
/**
|
|
42
|
-
* @param initial - Начальный статус (по умолчанию 'valid')
|
|
43
|
-
*/
|
|
44
|
-
constructor(initial?: FieldStatus);
|
|
45
|
-
/**
|
|
46
|
-
* Начать валидацию
|
|
47
|
-
*
|
|
48
|
-
* Переводит статус в 'pending' если поле не отключено
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```typescript
|
|
52
|
-
* statusMachine.startValidation();
|
|
53
|
-
* // status: 'pending'
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
startValidation(): void;
|
|
57
|
-
/**
|
|
58
|
-
* Завершить валидацию
|
|
59
|
-
*
|
|
60
|
-
* @param hasErrors - Есть ли ошибки валидации
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```typescript
|
|
64
|
-
* // Валидация успешна
|
|
65
|
-
* statusMachine.completeValidation(false);
|
|
66
|
-
* // status: 'valid'
|
|
67
|
-
*
|
|
68
|
-
* // Есть ошибки
|
|
69
|
-
* statusMachine.completeValidation(true);
|
|
70
|
-
* // status: 'invalid'
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
completeValidation(hasErrors: boolean): void;
|
|
74
|
-
/**
|
|
75
|
-
* Установить ошибки напрямую (без перехода через pending)
|
|
76
|
-
*
|
|
77
|
-
* Используется для синхронной валидации или установки ошибок извне
|
|
78
|
-
*
|
|
79
|
-
* @param hasErrors - Есть ли ошибки
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* ```typescript
|
|
83
|
-
* statusMachine.setErrors(true);
|
|
84
|
-
* // status: 'invalid'
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
setErrors(hasErrors: boolean): void;
|
|
88
|
-
/**
|
|
89
|
-
* Отключить поле
|
|
90
|
-
*
|
|
91
|
-
* Переводит статус в 'disabled'
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```typescript
|
|
95
|
-
* statusMachine.disable();
|
|
96
|
-
* // status: 'disabled'
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
disable(): void;
|
|
100
|
-
/**
|
|
101
|
-
* Включить поле
|
|
102
|
-
*
|
|
103
|
-
* @param hasErrors - Есть ли ошибки (определяет valid/invalid)
|
|
104
|
-
*
|
|
105
|
-
* @example
|
|
106
|
-
* ```typescript
|
|
107
|
-
* statusMachine.enable(false);
|
|
108
|
-
* // status: 'valid'
|
|
109
|
-
*
|
|
110
|
-
* statusMachine.enable(true);
|
|
111
|
-
* // status: 'invalid'
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
enable(hasErrors?: boolean): void;
|
|
115
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { ComponentType } from 'react';
|
|
2
|
-
import { Signal } from '@preact/signals-core';
|
|
3
|
-
import { ValidatorFn, AsyncValidatorFn, AnyFunction } from './index';
|
|
4
|
-
/**
|
|
5
|
-
* Конфигурация поля
|
|
6
|
-
* @group Types
|
|
7
|
-
* @category Configuration Types
|
|
8
|
-
* @remarks
|
|
9
|
-
* В схеме `createForm({ model, schema })` лист связывают через **`value: model.$.field`**: harvest
|
|
10
|
-
* ловит узел по идентичности `Signal` (`node.value instanceof Signal` — см. `create-form.ts`) и
|
|
11
|
-
* нормализует его в `valueSignal`. Писать `valueSignal:` **в самой схеме `createForm` нельзя** —
|
|
12
|
-
* harvest его не подхватит (проверка идёт по `node.value`), и `component`/`componentProps` этого
|
|
13
|
-
* узла молча отбросятся. `value`/`valueSignal` на прямом FieldNode-конфиге — это
|
|
14
|
-
* post-normalization / legacy-слой, а не форма записи в схеме `createForm`.
|
|
15
|
-
*/
|
|
16
|
-
export interface FieldConfig<T> {
|
|
17
|
-
/**
|
|
18
|
-
* Начальное значение-литерал (legacy-путь). Под архитектурой M1 значение приходит из
|
|
19
|
-
* {@link FieldConfig.valueSignal} (сигнал {@link FormModel}); тогда `value` не требуется.
|
|
20
|
-
*/
|
|
21
|
-
value?: T | null;
|
|
22
|
-
/**
|
|
23
|
-
* Сигнал значения из {@link FormModel} (M1). Если задан — служит источником истины значения
|
|
24
|
-
* поля (нода не владеет значением, а ссылается на этот сигнал). Имеет приоритет над `value`.
|
|
25
|
-
*/
|
|
26
|
-
valueSignal?: Signal<T>;
|
|
27
|
-
/**
|
|
28
|
-
* UI-компонент поля. Опционален: core-часть можно использовать без ссылки на компонент
|
|
29
|
-
* (значение/валидация работают без UI; компонент нужен только для рендеринга).
|
|
30
|
-
*/
|
|
31
|
-
component?: ComponentType<any>;
|
|
32
|
-
componentProps?: any;
|
|
33
|
-
validators?: ValidatorFn<T>[];
|
|
34
|
-
asyncValidators?: AsyncValidatorFn<T>[];
|
|
35
|
-
disabled?: boolean;
|
|
36
|
-
updateOn?: 'change' | 'blur' | 'submit';
|
|
37
|
-
/** Задержка (в мс) перед запуском асинхронной валидации */
|
|
38
|
-
debounce?: number;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* «Хотя бы одно поле обязано присутствовать»: превращает объект со всеми опциональными
|
|
42
|
-
* свойствами в объединение вариантов, где ровно один ключ обязателен, а остальные опциональны.
|
|
43
|
-
* Пустой объект `{}` не assignable ни к одному варианту и потому отклоняется.
|
|
44
|
-
*
|
|
45
|
-
* @internal
|
|
46
|
-
*/
|
|
47
|
-
type RequireAtLeastOne<T> = {
|
|
48
|
-
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
|
|
49
|
-
}[keyof T];
|
|
50
|
-
/**
|
|
51
|
-
* {@link FieldConfig}, из которого нельзя сделать пустой `{}`.
|
|
52
|
-
*
|
|
53
|
-
* Все свойства {@link FieldConfig} опциональны, поэтому в {@link FormSchema} опечатка вида
|
|
54
|
-
* `{ email: {} }` молча проходит проверку типов и в рантайме становится пустой подгруппой
|
|
55
|
-
* вместо поля. `NonEmptyFieldConfig` требует наличия хотя бы одного свойства (value/valueSignal/
|
|
56
|
-
* component/validators/…), оставляя каждое из них опциональным по отдельности.
|
|
57
|
-
*
|
|
58
|
-
* @internal
|
|
59
|
-
*/
|
|
60
|
-
type NonEmptyFieldConfig<T> = RequireAtLeastOne<FieldConfig<T>>;
|
|
61
|
-
/**
|
|
62
|
-
* **Data-shaped** конфиг формы: ключи повторяют структуру данных `T`, значения — {@link FieldConfig}
|
|
63
|
-
* (или вложенный `FormSchema`). Форма конфига, из которой строится {@link GroupNode}.
|
|
64
|
-
* - `T[] -> [FormSchema<T>]` (массив с одним элементом)
|
|
65
|
-
* - `object -> FormSchema<T>` (группа)
|
|
66
|
-
* - `primitive -> FieldConfig<T>` (поле)
|
|
67
|
-
*
|
|
68
|
-
* Использует NonNullable для корректной обработки опциональных полей.
|
|
69
|
-
*
|
|
70
|
-
* ⚠️ Не путать с {@link FormSchemaNode} — тот описывает **узел дерева** M1-схемы (лист/массив/
|
|
71
|
-
* контейнер), передаваемой в `createForm({ model, schema })`. `FormSchema` — это data-shaped конфиг
|
|
72
|
-
* (ключи = поля данных), а не узел дерева.
|
|
73
|
-
*
|
|
74
|
-
* @group Types
|
|
75
|
-
* @category Configuration Types
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```typescript
|
|
79
|
-
* interface Form {
|
|
80
|
-
* name: string; // → FieldConfig<string>
|
|
81
|
-
* address: { // → FormSchema<Address>
|
|
82
|
-
* city: string;
|
|
83
|
-
* street: string;
|
|
84
|
-
* };
|
|
85
|
-
* items?: Array<{ // → [FormSchema<Item>] (опциональный)
|
|
86
|
-
* title: string;
|
|
87
|
-
* price: number;
|
|
88
|
-
* }>;
|
|
89
|
-
* }
|
|
90
|
-
*
|
|
91
|
-
* const schema: FormSchema<Form> = {
|
|
92
|
-
* name: { value: '', component: Input },
|
|
93
|
-
* address: {
|
|
94
|
-
* city: { value: '', component: Input },
|
|
95
|
-
* street: { value: '', component: Input },
|
|
96
|
-
* },
|
|
97
|
-
* items: [{
|
|
98
|
-
* title: { value: '', component: Input },
|
|
99
|
-
* price: { value: 0, component: Input },
|
|
100
|
-
* }],
|
|
101
|
-
* };
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
export type FormSchema<T> = {
|
|
105
|
-
[K in keyof T]: NonNullable<T[K]> extends string | number | boolean ? NonEmptyFieldConfig<T[K]> : NonNullable<T[K]> extends Array<infer U> ? U extends string | number | boolean ? NonEmptyFieldConfig<T[K]> : U extends Date | File | Blob | AnyFunction ? NonEmptyFieldConfig<T[K]> : [FormSchema<U>] : NonNullable<T[K]> extends Date | File | Blob | AnyFunction ? NonEmptyFieldConfig<T[K]> : FormSchema<NonNullable<T[K]>>;
|
|
106
|
-
};
|
|
107
|
-
export {};
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { FieldNode } from '../nodes/field-node';
|
|
2
|
-
import { GroupNode } from '../nodes/group-node';
|
|
3
|
-
import { ArrayNode } from '../nodes/array-node';
|
|
4
|
-
/**
|
|
5
|
-
* Признак «объект-группа» — обычный объект, который нужно обернуть в под-форму.
|
|
6
|
-
*
|
|
7
|
-
* Исключает массивы, спец-объекты (`Date`/`File`/`Blob`) и примитивы.
|
|
8
|
-
* Используется как дискриминатор вместо `extends FormFields` (`Record<string, FormValue>`):
|
|
9
|
-
* пользовательские модели почти всегда объявлены через `interface`, который НЕ assignable
|
|
10
|
-
* к Record-типу (у интерфейса нет неявной index signature), и потому ошибочно проваливался
|
|
11
|
-
* бы в ветку `FieldNode`. Проверка `extends object` корректно ловит и `interface`, и `type`.
|
|
12
|
-
*
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
type IsGroupObject<V> = V extends ReadonlyArray<unknown> ? false : V extends Date | File | Blob ? false : V extends (...args: never[]) => unknown ? false : V extends object ? true : false;
|
|
16
|
-
/**
|
|
17
|
-
* Мапит тип модели данных T на правильные типы узлов формы
|
|
18
|
-
*
|
|
19
|
-
* Рекурсивно определяет типы узлов на основе структуры данных:
|
|
20
|
-
* - `T[K] extends Array<infer U>` где U - объект → `FormArrayProxy<U>`
|
|
21
|
-
* - `T[K] extends Array<infer U>` где U - примитив → `FieldNode<T[K]>` (массив как обычное поле)
|
|
22
|
-
* - `T[K] extends object` → `FormProxy<T[K]>` (вложенная форма с типизацией)
|
|
23
|
-
* - `T[K]` примитив → `FieldNode<T[K]>` (простое поле)
|
|
24
|
-
*
|
|
25
|
-
* Использует NonNullable для правильной обработки опциональных полей
|
|
26
|
-
*
|
|
27
|
-
* @group Types
|
|
28
|
-
* @category Proxy Types
|
|
29
|
-
*
|
|
30
|
-
* @template T - Тип модели данных формы
|
|
31
|
-
*/
|
|
32
|
-
export type FormControlsProxy<T> = {
|
|
33
|
-
[K in keyof T]-?: NonNullable<T[K]> extends ReadonlyArray<infer U> ? IsGroupObject<U> extends true ? FormArrayProxy<U & object> : FieldNode<T[K]> : IsGroupObject<NonNullable<T[K]>> extends true ? FormProxy<NonNullable<T[K]>> : FieldNode<T[K]>;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Комбинированный тип для GroupNode с Proxy доступом к полям
|
|
37
|
-
*
|
|
38
|
-
* Объединяет методы и свойства GroupNode с типизированными полями формы.
|
|
39
|
-
* Это позволяет использовать как API GroupNode, так и прямой доступ к полям.
|
|
40
|
-
*
|
|
41
|
-
* @group Types
|
|
42
|
-
* @category Proxy Types
|
|
43
|
-
*
|
|
44
|
-
* @template T - Тип модели данных формы
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```typescript
|
|
48
|
-
* interface UserForm {
|
|
49
|
-
* email: string;
|
|
50
|
-
* profile: {
|
|
51
|
-
* name: string;
|
|
52
|
-
* age: number;
|
|
53
|
-
* };
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* const form = createForm<UserForm>(schema);
|
|
57
|
-
*
|
|
58
|
-
* // Доступ к методам GroupNode
|
|
59
|
-
* await form.validate();
|
|
60
|
-
* const values = form.getValue();
|
|
61
|
-
* console.log(form.valid.value);
|
|
62
|
-
*
|
|
63
|
-
* // Прямой доступ к полям (через Proxy)
|
|
64
|
-
* form.email.setValue('test@mail.com');
|
|
65
|
-
* form.profile.name.setValue('John');
|
|
66
|
-
* ```
|
|
67
|
-
*
|
|
68
|
-
* @remarks
|
|
69
|
-
* **Зарезервированные имена.** Прямой доступ `form.<name>` отдаёт приоритет собственным
|
|
70
|
-
* членам {@link GroupNode} (`value`, `status`, `id`, `errors`, `disabled`, `reset`, `submit`, …).
|
|
71
|
-
* Поэтому поле модели, названное любым из этих имён, недостижимо как `form.<name>` — там будет
|
|
72
|
-
* агрегированный сигнал/метод узла, а не {@link FieldNode}. Такие имена исключены из полевого
|
|
73
|
-
* пространства имён на уровне типов (через `Omit<…, keyof GroupNode>`), чтобы затенение не
|
|
74
|
-
* компилировалось молча. Для доступа к любому полю по имени — включая затенённые — используйте
|
|
75
|
-
* escape-hatch пространство {@link FormProxy.$}: `form.$.status`, `form.$.id`, `form.$.value`.
|
|
76
|
-
*/
|
|
77
|
-
export type FormProxy<T> = GroupNode<T> & Omit<FormControlsProxy<T>, keyof GroupNode<T>> & {
|
|
78
|
-
/**
|
|
79
|
-
* Escape-hatch пространство имён «controls»: типобезопасный доступ ко ВСЕМ полям формы
|
|
80
|
-
* по имени, включая поля, чьи имена совпадают с членами {@link GroupNode}
|
|
81
|
-
* (`value`/`status`/`id`/`errors`/…), недостижимые через `form.<name>`.
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```typescript
|
|
85
|
-
* // модель: { status: string; email: string }
|
|
86
|
-
* form.status; // ReadonlySignal<FieldStatus> — агрегат GroupNode (не поле!)
|
|
87
|
-
* form.$.status; // FieldNode<string> — поле пользователя
|
|
88
|
-
* form.$.status.setValue('active');
|
|
89
|
-
* form.$.email; // FieldNode<string> — так же доступны и незатенённые поля
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
|
-
readonly $: FormControlsProxy<T>;
|
|
93
|
-
};
|
|
94
|
-
/**
|
|
95
|
-
* Комбинированный тип для ArrayNode с Proxy доступом к элементам
|
|
96
|
-
*
|
|
97
|
-
* Объединяет методы и свойства ArrayNode с типизированным доступом к элементам массива.
|
|
98
|
-
*
|
|
99
|
-
* @group Types
|
|
100
|
-
* @category Proxy Types
|
|
101
|
-
*
|
|
102
|
-
* @template T - Тип модели данных элемента массива
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* ```typescript
|
|
106
|
-
* interface TodoItem {
|
|
107
|
-
* title: string;
|
|
108
|
-
* completed: boolean;
|
|
109
|
-
* }
|
|
110
|
-
*
|
|
111
|
-
* const todos: FormArrayProxy<TodoItem> = new ArrayNode(schema);
|
|
112
|
-
*
|
|
113
|
-
* // Доступ к методам ArrayNode
|
|
114
|
-
* todos.push({ title: 'New todo', completed: false });
|
|
115
|
-
* todos.removeAt(0);
|
|
116
|
-
*
|
|
117
|
-
* // Доступ к элементам (через Proxy)
|
|
118
|
-
* todos.at(0)?.title.setValue('Updated title');
|
|
119
|
-
*
|
|
120
|
-
* // Итерация
|
|
121
|
-
* todos.forEach((item, i) => {
|
|
122
|
-
* console.log(item.title.value.value);
|
|
123
|
-
* });
|
|
124
|
-
* ```
|
|
125
|
-
*/
|
|
126
|
-
export type FormArrayProxy<T extends object> = ArrayNode<T> & {
|
|
127
|
-
/**
|
|
128
|
-
* Безопасный доступ к элементу массива по индексу
|
|
129
|
-
* Возвращает GroupNode с типизированными полями или undefined
|
|
130
|
-
*/
|
|
131
|
-
at(index: number): FormProxy<T> | undefined;
|
|
132
|
-
/**
|
|
133
|
-
* Итерация по элементам массива с типизированными элементами
|
|
134
|
-
*/
|
|
135
|
-
forEach(callback: (item: FormProxy<T>, index: number) => void): void;
|
|
136
|
-
/**
|
|
137
|
-
* Маппинг элементов массива с типизированными элементами
|
|
138
|
-
*/
|
|
139
|
-
map<R>(callback: (item: FormProxy<T>, index: number) => R): R[];
|
|
140
|
-
};
|
|
141
|
-
export {};
|