@reformer/core 11.0.0-beta.2 → 11.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/behaviors.d.ts +1 -1
- package/dist/behaviors.js +37 -37
- package/dist/create-model-Dxh9cfLp.js +97 -0
- package/dist/form/behaviors/collections.d.ts +50 -0
- package/dist/form/behaviors/context.d.ts +23 -0
- package/dist/form/behaviors/index.d.ts +28 -0
- package/dist/form/behaviors/internals.d.ts +26 -0
- package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +3 -3
- package/dist/form/behaviors/operators.d.ts +51 -0
- package/dist/form/behaviors/types.d.ts +17 -0
- package/dist/form/create-core-form.d.ts +70 -0
- package/dist/form/create-form.d.ts +1 -1
- package/dist/form/index.d.ts +12 -12
- package/dist/form/nodes/model-array-node.d.ts +1 -1
- package/dist/form/types/contracts.d.ts +9 -6
- package/dist/form/types/validation-schema.d.ts +1 -1
- package/dist/form/validation/config.d.ts +73 -0
- package/dist/form/validation/context.d.ts +27 -0
- package/dist/form/{error-handler.d.ts → validation/error-handler.d.ts} +1 -1
- package/dist/form/validation/index.d.ts +23 -28
- package/dist/form/validation/operators.d.ts +42 -0
- package/dist/form/validation/run.d.ts +45 -0
- package/dist/form/{validation-strategy.d.ts → validation/strategy.d.ts} +10 -4
- package/dist/form/validation/types.d.ts +28 -0
- package/dist/form/{validation/validators → validators}/date-utils.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/email.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/file-type.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/file-utils.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/future-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/index.d.ts +6 -4
- package/dist/form/{validation/validators → validators}/integer.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/is-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/is-number.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-age.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-file-size.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-files.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-length.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max-total-file-size.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/max.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-age.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-file-size.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-files.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min-length.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/min.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/multiple-of.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/non-negative.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/non-zero.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/past-date.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/pattern.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/phone.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/required.d.ts +1 -1
- package/dist/form/{validation/validators → validators}/url.d.ts +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +708 -479
- package/dist/{state/form-model.d.ts → model/create-model.d.ts} +0 -14
- package/dist/{state → model}/index.d.ts +10 -11
- package/dist/model/model-nodes.d.ts +60 -0
- package/dist/model/model-signals-proxy.d.ts +32 -0
- package/dist/model/model-value-proxy.d.ts +3 -0
- package/dist/{state → model}/safe-effect.d.ts +1 -1
- package/dist/model-signals-proxy-mV3WGdlO.js +233 -0
- package/dist/model.d.ts +2 -0
- package/dist/model.js +21 -0
- package/dist/{form → platforms/react}/hooks/types.d.ts +1 -1
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +26 -0
- package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +3 -3
- package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +1 -1
- package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +3 -3
- package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +2 -2
- package/dist/platforms/react/index.d.ts +23 -0
- package/dist/signals.d.ts +1 -1
- package/dist/strategy-DA3JwxsR.js +170 -0
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +10 -165
- package/dist/validators/email.d.ts +1 -1
- package/dist/validators/future-date.d.ts +1 -1
- package/dist/validators/integer.d.ts +1 -1
- package/dist/validators/is-date.d.ts +1 -1
- package/dist/validators/is-number.d.ts +1 -1
- package/dist/validators/max-age.d.ts +1 -1
- package/dist/validators/max-date.d.ts +1 -1
- package/dist/validators/max-length.d.ts +1 -1
- package/dist/validators/max.d.ts +1 -1
- package/dist/validators/min-age.d.ts +1 -1
- package/dist/validators/min-date.d.ts +1 -1
- package/dist/validators/min-length.d.ts +1 -1
- package/dist/validators/min.d.ts +1 -1
- package/dist/validators/multiple-of.d.ts +1 -1
- package/dist/validators/non-negative.d.ts +1 -1
- package/dist/validators/non-zero.d.ts +1 -1
- package/dist/validators/past-date.d.ts +1 -1
- package/dist/validators/pattern.d.ts +1 -1
- package/dist/validators/phone.d.ts +1 -1
- package/dist/validators/required.d.ts +1 -1
- package/dist/validators/url.d.ts +1 -1
- package/dist/validators.d.ts +1 -1
- package/docs/llms/02-quick-start.md +14 -7
- package/docs/llms/15-project-structure.md +12 -8
- package/llms.txt +329 -53
- package/package.json +4 -4
- package/dist/form/behaviors.d.ts +0 -133
- package/dist/form/validation-schema.d.ts +0 -106
- package/dist/form/validators.d.ts +0 -11
- package/dist/form-model-DhW2HZOB.js +0 -323
- package/dist/state.d.ts +0 -2
- package/dist/state.js +0 -21
- package/dist/subscription-manager-705bls97.js +0 -171
- /package/dist/{state → form/nodes}/subscription-manager.d.ts +0 -0
- /package/dist/{state → model}/behaviors-value.d.ts +0 -0
- /package/dist/{state → model}/derived-registry.d.ts +0 -0
- /package/dist/{state → model}/types.d.ts +0 -0
- /package/dist/{behaviors-node-DGEFMTQu.js → node-DGEFMTQu.js} +0 -0
- /package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +0 -0
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import { FormModel, PathAwareSignal } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Узел дерева `model.$` — контейнер (группа/массив), а не лист?
|
|
4
|
-
*
|
|
5
|
-
* Контейнерный узел структурно совместим с `ReadonlySignal` (`peek`/`value`/`subscribe`), поэтому
|
|
6
|
-
* duck-typing «есть `peek` ⇒ это лист» на нём даёт ложное срабатывание. Этот guard — надёжный
|
|
7
|
-
* способ различить: обходчикам дерева нужно спускаться в контейнер, а не читать его целиком.
|
|
8
|
-
*
|
|
9
|
-
* @group Model
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* const isLeaf = (v: unknown) => isSignalLike(v) && !isModelContainerSignal(v);
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export declare function isModelContainerSignal(value: unknown): boolean;
|
|
16
2
|
/**
|
|
17
3
|
* Обойти ВСЕ листовые сигналы модели (включая элементы массивов), вызвав `visit` на каждом.
|
|
18
4
|
*
|
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@reformer/core/
|
|
2
|
+
* `@reformer/core/model` — низкоуровневый реактивный субстрат (M1) и barrel модуля `model`.
|
|
3
3
|
*
|
|
4
4
|
* Это «прокси-объект с сигналами» и инструментарий для построения доменных модулей поверх него
|
|
5
5
|
* (form, table, …). Модуль СТРОГО реактивный: НИЧЕГО связанного с формами/валидацией/схемами здесь
|
|
6
|
-
* нет — только модель, value-операции, producer-флаг и утилиты субстрата. Граница
|
|
7
|
-
* зафиксирована ESLint-правилом (`no-restricted-imports` на `src/
|
|
6
|
+
* нет — только модель, value-операции, producer-флаг и утилиты субстрата. Граница model⇏form
|
|
7
|
+
* зафиксирована ESLint-правилом (`no-restricted-imports` на `src/model/**`).
|
|
8
8
|
*
|
|
9
9
|
* Реактивный рантайм (`signal/computed/effect/…`) живёт в отдельном subpath `@reformer/core/signals`
|
|
10
10
|
* (единый владелец `@preact/signals-core`) — импортируй его оттуда; здесь он НЕ реэкспортируется,
|
|
11
11
|
* чтобы зонтичный barrel `.` не разрастался рантаймом.
|
|
12
12
|
*
|
|
13
|
-
* Состав (реэкспорты — из тех же файлов, что использует зонтик `.`, поэтому `.` и `/
|
|
13
|
+
* Состав (реэкспорты — из тех же файлов, что использует зонтик `.`, поэтому `.` и `/model` — один
|
|
14
14
|
* module-инстанс: общий `derived`-WeakMap, единая идентичность `Signal`):
|
|
15
15
|
* - **модель** — `createModel` + типы `FormModel`/`ModelArray`/… + `PathAwareSignal`;
|
|
16
|
+
* - **обход листьев** — `eachLeafSignal` (подписка «любое поле изменилось»);
|
|
16
17
|
* - **value-операции** (реактивные правила) — `computeFrom`/`copyFrom`/`watchField`/`transformValue`/
|
|
17
18
|
* `resetWhen`/`syncFields`/`revalidateWhen`;
|
|
18
19
|
* - **producer-owned флаг** — `markDerived`/`isDerived`/`unmarkDerived`;
|
|
19
|
-
* - **утилиты субстрата** — `runOutsideEffect`/`safeCallback`/`safeDebouncedCallback
|
|
20
|
-
* `SubscriptionManager`.
|
|
20
|
+
* - **утилиты субстрата** — `runOutsideEffect`/`safeCallback`/`safeDebouncedCallback`.
|
|
21
21
|
*
|
|
22
|
-
* @group
|
|
23
|
-
* @module
|
|
22
|
+
* @group Model
|
|
23
|
+
* @module model
|
|
24
24
|
*/
|
|
25
|
-
export { createModel } from './
|
|
25
|
+
export { createModel, eachLeafSignal } from './create-model';
|
|
26
26
|
export type { FormModel, ModelArray, ModelArraySignals, ModelGroupSignals, ModelObject, ModelValue, ModelSignals, ModelApi, PathAwareSignal, } from './types';
|
|
27
|
-
export { isModelContainerSignal } from './
|
|
27
|
+
export { isModelContainerSignal } from './model-signals-proxy';
|
|
28
28
|
export { computeFrom, copyFrom, watchField, transformValue, resetWhen, syncFields, revalidateWhen, } from './behaviors-value';
|
|
29
29
|
export type { BehaviorCleanup } from './behaviors-value';
|
|
30
30
|
export { markDerived, isDerived, unmarkDerived } from './derived-registry';
|
|
31
31
|
export { runOutsideEffect, safeCallback, safeDebouncedCallback } from './safe-effect';
|
|
32
|
-
export { SubscriptionManager } from './subscription-manager';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Signal } from '@preact/signals-core';
|
|
2
|
+
import { PathAwareSignal } from './types';
|
|
3
|
+
export declare const isIndexKey: (key: string) => boolean;
|
|
4
|
+
export type ModelNode = LeafNode | GroupNode | ArrayNode;
|
|
5
|
+
declare class LeafNode {
|
|
6
|
+
path: string;
|
|
7
|
+
readonly kind: "leaf";
|
|
8
|
+
readonly signal: PathAwareSignal<unknown>;
|
|
9
|
+
private initial;
|
|
10
|
+
constructor(initial: unknown, path: string);
|
|
11
|
+
rebase(path: string): void;
|
|
12
|
+
/** Реактивное чтение (внутри effect/computed создаёт зависимость). */
|
|
13
|
+
read(): unknown;
|
|
14
|
+
/** Нереактивный снимок. */
|
|
15
|
+
peek(): unknown;
|
|
16
|
+
set(value: unknown): void;
|
|
17
|
+
resetToInitial(): void;
|
|
18
|
+
captureInitial(): void;
|
|
19
|
+
dirty(): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class GroupNode {
|
|
22
|
+
path: string;
|
|
23
|
+
readonly kind: "group";
|
|
24
|
+
readonly children: Map<string, ModelNode>;
|
|
25
|
+
constructor(initial: Record<string, unknown>, path: string);
|
|
26
|
+
rebase(path: string): void;
|
|
27
|
+
/** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
|
|
28
|
+
read(): Record<string, unknown>;
|
|
29
|
+
peek(): Record<string, unknown>;
|
|
30
|
+
set(value: unknown): void;
|
|
31
|
+
resetToInitial(): void;
|
|
32
|
+
captureInitial(): void;
|
|
33
|
+
dirty(): boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare class ArrayNode {
|
|
36
|
+
path: string;
|
|
37
|
+
readonly kind: "array";
|
|
38
|
+
readonly items: Signal<ModelNode[]>;
|
|
39
|
+
private initial;
|
|
40
|
+
constructor(initial: unknown[], path: string);
|
|
41
|
+
rebase(path: string): void;
|
|
42
|
+
private reindex;
|
|
43
|
+
push(value: unknown): void;
|
|
44
|
+
insertAt(index: number, value: unknown): void;
|
|
45
|
+
removeAt(index: number): void;
|
|
46
|
+
move(from: number, to: number): void;
|
|
47
|
+
swap(a: number, b: number): void;
|
|
48
|
+
clear(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Реактивное чтение массива. Читаем `items.value` (а не `.peek()`) — внутри effect/computed это
|
|
51
|
+
* подписка на СОСТАВ массива, поэтому push/removeAt/move ретригерят наравне с правкой элемента.
|
|
52
|
+
*/
|
|
53
|
+
read(): unknown[];
|
|
54
|
+
peek(): unknown[];
|
|
55
|
+
set(value: unknown): void;
|
|
56
|
+
resetToInitial(): void;
|
|
57
|
+
captureInitial(): void;
|
|
58
|
+
dirty(): boolean;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PathAwareSignal } from './types';
|
|
2
|
+
import { ModelNode, GroupNode } from './model-nodes';
|
|
3
|
+
/**
|
|
4
|
+
* Узел дерева `$`: лист → сам {@link PathAwareSignal}, группа/массив → контейнерный узел —
|
|
5
|
+
* {@link ReadonlySignal} агрегированного значения ПЛЮС доступ к детям по имени/индексу.
|
|
6
|
+
*
|
|
7
|
+
* Порядок разрешения ключа: служебные `__path`/`__kind` → ребёнок → свойство сигнала. Дети идут
|
|
8
|
+
* раньше свойств сигнала — тот же приоритет, что у {@link makeFormModel} (поле формы затеняет метод
|
|
9
|
+
* API). Поэтому имена `value`/`peek`/`subscribe`/`valueOf`/`toString`/`toJSON`/`brand` де-факто
|
|
10
|
+
* зарезервированы: одноимённое поле формы затенит свойство сигнала (`subscribe` при этом продолжает
|
|
11
|
+
* работать — он замкнут на агрегат, а не читает `.value` через прокси).
|
|
12
|
+
*
|
|
13
|
+
* `has`/`ownKeys` намеренно НЕ показывают свойства сигнала — ровно как `__path`, доступный через
|
|
14
|
+
* `get`, но невидимый для `in`/`Object.keys`. Так `Object.keys(model.$.<group>)` остаётся списком
|
|
15
|
+
* полей, а потребители, перечисляющие модель, не видят служебных ключей.
|
|
16
|
+
*/
|
|
17
|
+
export declare function signalsProxy(node: ModelNode): any;
|
|
18
|
+
/**
|
|
19
|
+
* Узел дерева `model.$` — контейнер (группа/массив), а не лист?
|
|
20
|
+
*
|
|
21
|
+
* Контейнерный узел структурно совместим с `ReadonlySignal` (`peek`/`value`/`subscribe`), поэтому
|
|
22
|
+
* duck-typing «есть `peek` ⇒ это лист» на нём даёт ложное срабатывание. Этот guard — надёжный
|
|
23
|
+
* способ различить: обходчикам дерева нужно спускаться в контейнер, а не читать его целиком.
|
|
24
|
+
*
|
|
25
|
+
* @group Model
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const isLeaf = (v: unknown) => isSignalLike(v) && !isModelContainerSignal(v);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function isModelContainerSignal(value: unknown): boolean;
|
|
32
|
+
export declare function resolveSignalAt(root: GroupNode, path: string): PathAwareSignal<unknown> | undefined;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { batch as m, signal as y, computed as b } from "@preact/signals-core";
|
|
2
|
+
const c = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
function R(t) {
|
|
4
|
+
c.set(t, (c.get(t) ?? 0) + 1);
|
|
5
|
+
}
|
|
6
|
+
function S(t) {
|
|
7
|
+
const e = c.get(t);
|
|
8
|
+
e !== void 0 && (e <= 1 ? c.delete(t) : c.set(t, e - 1));
|
|
9
|
+
}
|
|
10
|
+
function A(t) {
|
|
11
|
+
return c.has(t);
|
|
12
|
+
}
|
|
13
|
+
const f = (t) => !(t === null || typeof t != "object" || Array.isArray(t) || t instanceof Date || typeof Blob < "u" && t instanceof Blob || typeof File < "u" && t instanceof File), o = (t, e) => t === "" ? String(e) : `${t}.${e}`, w = (t) => /^\d+$/.test(t), l = (t) => {
|
|
14
|
+
if (Array.isArray(t)) return t.map(l);
|
|
15
|
+
if (f(t)) {
|
|
16
|
+
const e = {};
|
|
17
|
+
for (const i of Object.keys(t)) e[i] = l(t[i]);
|
|
18
|
+
return e;
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
}, h = (t, e) => {
|
|
22
|
+
if (t === e) return !0;
|
|
23
|
+
if (Array.isArray(t) && Array.isArray(e))
|
|
24
|
+
return t.length !== e.length ? !1 : t.every((i, r) => h(i, e[r]));
|
|
25
|
+
if (f(t) && f(e)) {
|
|
26
|
+
const i = Object.keys(t), r = Object.keys(e);
|
|
27
|
+
return i.length !== r.length ? !1 : i.every((s) => h(t[s], e[s]));
|
|
28
|
+
}
|
|
29
|
+
return !1;
|
|
30
|
+
};
|
|
31
|
+
class _ {
|
|
32
|
+
constructor(e, i) {
|
|
33
|
+
this.path = i;
|
|
34
|
+
const r = y(e);
|
|
35
|
+
r.__path = i, this.signal = r, this.initial = l(e);
|
|
36
|
+
}
|
|
37
|
+
kind = "leaf";
|
|
38
|
+
signal;
|
|
39
|
+
initial;
|
|
40
|
+
rebase(e) {
|
|
41
|
+
this.path = e, this.signal.__path = e;
|
|
42
|
+
}
|
|
43
|
+
/** Реактивное чтение (внутри effect/computed создаёт зависимость). */
|
|
44
|
+
read() {
|
|
45
|
+
return this.signal.value;
|
|
46
|
+
}
|
|
47
|
+
/** Нереактивный снимок. */
|
|
48
|
+
peek() {
|
|
49
|
+
return this.signal.peek();
|
|
50
|
+
}
|
|
51
|
+
set(e) {
|
|
52
|
+
this.signal.value = e;
|
|
53
|
+
}
|
|
54
|
+
resetToInitial() {
|
|
55
|
+
this.signal.value = l(this.initial);
|
|
56
|
+
}
|
|
57
|
+
captureInitial() {
|
|
58
|
+
this.initial = l(this.signal.peek());
|
|
59
|
+
}
|
|
60
|
+
dirty() {
|
|
61
|
+
return !h(this.signal.peek(), this.initial);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class j {
|
|
65
|
+
constructor(e, i) {
|
|
66
|
+
this.path = i;
|
|
67
|
+
for (const r of Object.keys(e))
|
|
68
|
+
this.children.set(r, u(e[r], o(i, r)));
|
|
69
|
+
}
|
|
70
|
+
kind = "group";
|
|
71
|
+
children = /* @__PURE__ */ new Map();
|
|
72
|
+
rebase(e) {
|
|
73
|
+
this.path = e;
|
|
74
|
+
for (const [i, r] of this.children) r.rebase(o(e, i));
|
|
75
|
+
}
|
|
76
|
+
/** Реактивное чтение поддерева (внутри effect/computed подписывает на все листья группы). */
|
|
77
|
+
read() {
|
|
78
|
+
const e = {};
|
|
79
|
+
for (const [i, r] of this.children) e[i] = r.read();
|
|
80
|
+
return e;
|
|
81
|
+
}
|
|
82
|
+
peek() {
|
|
83
|
+
const e = {};
|
|
84
|
+
for (const [i, r] of this.children) e[i] = r.peek();
|
|
85
|
+
return e;
|
|
86
|
+
}
|
|
87
|
+
set(e) {
|
|
88
|
+
if (e == null || typeof e != "object") return;
|
|
89
|
+
const i = e;
|
|
90
|
+
m(() => {
|
|
91
|
+
for (const [r, s] of this.children)
|
|
92
|
+
r in i && (s.kind === "leaf" && A(s.signal) || s.set(i[r]));
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
resetToInitial() {
|
|
96
|
+
m(() => {
|
|
97
|
+
for (const e of this.children.values()) e.resetToInitial();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
captureInitial() {
|
|
101
|
+
for (const e of this.children.values()) e.captureInitial();
|
|
102
|
+
}
|
|
103
|
+
dirty() {
|
|
104
|
+
for (const e of this.children.values()) if (e.dirty()) return !0;
|
|
105
|
+
return !1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
class x {
|
|
109
|
+
constructor(e, i) {
|
|
110
|
+
this.path = i, this.items = y(e.map((r, s) => u(r, o(i, s)))), this.initial = l(e);
|
|
111
|
+
}
|
|
112
|
+
kind = "array";
|
|
113
|
+
items;
|
|
114
|
+
initial;
|
|
115
|
+
rebase(e) {
|
|
116
|
+
this.path = e, this.items.peek().forEach((i, r) => i.rebase(o(e, r)));
|
|
117
|
+
}
|
|
118
|
+
reindex() {
|
|
119
|
+
this.items.peek().forEach((e, i) => e.rebase(o(this.path, i)));
|
|
120
|
+
}
|
|
121
|
+
push(e) {
|
|
122
|
+
const i = this.items.peek();
|
|
123
|
+
this.items.value = [...i, u(e, o(this.path, i.length))];
|
|
124
|
+
}
|
|
125
|
+
insertAt(e, i) {
|
|
126
|
+
const r = [...this.items.peek()];
|
|
127
|
+
r.splice(e, 0, u(i, o(this.path, e))), this.items.value = r, this.reindex();
|
|
128
|
+
}
|
|
129
|
+
removeAt(e) {
|
|
130
|
+
const i = [...this.items.peek()];
|
|
131
|
+
i.splice(e, 1), this.items.value = i, this.reindex();
|
|
132
|
+
}
|
|
133
|
+
move(e, i) {
|
|
134
|
+
const r = [...this.items.peek()], [s] = r.splice(e, 1);
|
|
135
|
+
s && r.splice(i, 0, s), this.items.value = r, this.reindex();
|
|
136
|
+
}
|
|
137
|
+
swap(e, i) {
|
|
138
|
+
const r = [...this.items.peek()];
|
|
139
|
+
e < 0 || i < 0 || e >= r.length || i >= r.length || e === i || ([r[e], r[i]] = [r[i], r[e]], this.items.value = r, this.reindex());
|
|
140
|
+
}
|
|
141
|
+
clear() {
|
|
142
|
+
this.items.value = [];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Реактивное чтение массива. Читаем `items.value` (а не `.peek()`) — внутри effect/computed это
|
|
146
|
+
* подписка на СОСТАВ массива, поэтому push/removeAt/move ретригерят наравне с правкой элемента.
|
|
147
|
+
*/
|
|
148
|
+
read() {
|
|
149
|
+
return this.items.value.map((e) => e.read());
|
|
150
|
+
}
|
|
151
|
+
peek() {
|
|
152
|
+
return this.items.peek().map((e) => e.peek());
|
|
153
|
+
}
|
|
154
|
+
set(e) {
|
|
155
|
+
const i = Array.isArray(e) ? e : [];
|
|
156
|
+
this.items.value = i.map((r, s) => u(r, o(this.path, s)));
|
|
157
|
+
}
|
|
158
|
+
resetToInitial() {
|
|
159
|
+
this.set(l(this.initial));
|
|
160
|
+
}
|
|
161
|
+
captureInitial() {
|
|
162
|
+
this.initial = l(this.peek());
|
|
163
|
+
}
|
|
164
|
+
dirty() {
|
|
165
|
+
return !h(this.peek(), this.initial);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function u(t, e) {
|
|
169
|
+
return Array.isArray(t) ? new x(t, e) : f(t) ? new j(t, e) : new _(t, e);
|
|
170
|
+
}
|
|
171
|
+
const k = /* @__PURE__ */ new WeakMap();
|
|
172
|
+
function I(t) {
|
|
173
|
+
const e = b(() => t.read());
|
|
174
|
+
return {
|
|
175
|
+
get value() {
|
|
176
|
+
return e.value;
|
|
177
|
+
},
|
|
178
|
+
peek: () => e.peek(),
|
|
179
|
+
subscribe: (i) => e.subscribe(i),
|
|
180
|
+
valueOf: () => e.value,
|
|
181
|
+
toString: () => String(e.value),
|
|
182
|
+
toJSON: () => e.value,
|
|
183
|
+
brand: e.brand
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function O(t) {
|
|
187
|
+
if (t.kind === "leaf") return t.signal;
|
|
188
|
+
const e = k.get(t);
|
|
189
|
+
if (e) return e;
|
|
190
|
+
const i = I(t), r = t.kind === "group", s = (a) => r ? t.children.get(a) : w(a) ? t.items.value[Number(a)] : void 0, d = r ? {} : {
|
|
191
|
+
get length() {
|
|
192
|
+
return t.items.value.length;
|
|
193
|
+
}
|
|
194
|
+
}, p = new Proxy(d, {
|
|
195
|
+
get: (a, n, v) => {
|
|
196
|
+
if (n === "__path") return t.path;
|
|
197
|
+
if (n === "__kind") return t.kind;
|
|
198
|
+
if (typeof n != "string") return Reflect.get(i, n, i);
|
|
199
|
+
const g = s(n);
|
|
200
|
+
return g ? O(g) : !r && Reflect.has(a, n) ? Reflect.get(a, n, v) : Reflect.get(i, n, i);
|
|
201
|
+
},
|
|
202
|
+
has: (a, n) => typeof n == "string" && (s(n) !== void 0 || !r && Reflect.has(a, n)),
|
|
203
|
+
ownKeys: () => r ? [...t.children.keys()] : Reflect.ownKeys(d),
|
|
204
|
+
getOwnPropertyDescriptor: (a, n) => typeof n == "string" && s(n) ? { enumerable: !0, configurable: !0 } : r ? void 0 : Reflect.getOwnPropertyDescriptor(a, n)
|
|
205
|
+
});
|
|
206
|
+
return k.set(t, p), p;
|
|
207
|
+
}
|
|
208
|
+
function D(t) {
|
|
209
|
+
if (t == null || typeof t != "object") return !1;
|
|
210
|
+
const e = t.__kind;
|
|
211
|
+
return e === "group" || e === "array";
|
|
212
|
+
}
|
|
213
|
+
function P(t, e) {
|
|
214
|
+
if (!e) return;
|
|
215
|
+
let i = t;
|
|
216
|
+
for (const r of e.split(".")) {
|
|
217
|
+
if (!i) return;
|
|
218
|
+
if (i.kind === "group") i = i.children.get(r);
|
|
219
|
+
else if (i.kind === "array") i = i.items.peek()[Number(r)];
|
|
220
|
+
else return;
|
|
221
|
+
}
|
|
222
|
+
return i && i.kind === "leaf" ? i.signal : void 0;
|
|
223
|
+
}
|
|
224
|
+
export {
|
|
225
|
+
j as G,
|
|
226
|
+
A as a,
|
|
227
|
+
w as b,
|
|
228
|
+
D as i,
|
|
229
|
+
R as m,
|
|
230
|
+
P as r,
|
|
231
|
+
O as s,
|
|
232
|
+
S as u
|
|
233
|
+
};
|
package/dist/model.d.ts
ADDED
package/dist/model.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { c as s, e as r } from "./create-model-Dxh9cfLp.js";
|
|
2
|
+
import { a as i, i as t, m as c, u as l } from "./model-signals-proxy-mV3WGdlO.js";
|
|
3
|
+
import { c as n, a as f, r as m, b as u, d as p, e as b, f as h, s as k, t as v, w as D } from "./behaviors-value-C7dSEhCc.js";
|
|
4
|
+
export {
|
|
5
|
+
n as computeFrom,
|
|
6
|
+
f as copyFrom,
|
|
7
|
+
s as createModel,
|
|
8
|
+
r as eachLeafSignal,
|
|
9
|
+
i as isDerived,
|
|
10
|
+
t as isModelContainerSignal,
|
|
11
|
+
c as markDerived,
|
|
12
|
+
m as resetWhen,
|
|
13
|
+
u as revalidateWhen,
|
|
14
|
+
p as runOutsideEffect,
|
|
15
|
+
b as safeCallback,
|
|
16
|
+
h as safeDebouncedCallback,
|
|
17
|
+
k as syncFields,
|
|
18
|
+
v as transformValue,
|
|
19
|
+
l as unmarkDerived,
|
|
20
|
+
D as watchField
|
|
21
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FormValidationController } from '../../../form/validation/strategy';
|
|
2
|
+
/** Минимум, который хук ожидает от бандла: опциональный контроллер живой валидации. */
|
|
3
|
+
export interface FormBundleLike {
|
|
4
|
+
validation?: {
|
|
5
|
+
controller: FormValidationController;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Собрать форму один раз и держать её стабильной; живую стратегию валидации армировать в эффекте
|
|
10
|
+
* (не при SSR — там эффекты не выполняются).
|
|
11
|
+
*
|
|
12
|
+
* @typeParam B - Тип бандла конкретной фабрики.
|
|
13
|
+
* @param factory - Фабрика бандла, обычно `() => createCoreForm({…})`.
|
|
14
|
+
* @returns Стабильный бандл.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const credit = useFormBundle(() => createCoreForm<CreditForm>({ model: createCreditModel() }));
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Передавайте именно фабрику: `useState` трактует функцию как ленивый инициализатор, а некоторые
|
|
23
|
+
* части бандла сами являются функциями (`RenderSchemaProxy`), поэтому `useFormBundle(bundle.render)`
|
|
24
|
+
* вызвал бы её вместо сохранения.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useFormBundle<B extends FormBundleLike>(factory: () => B): B;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FormModel } from '
|
|
2
|
-
import { ValidationSchema } from '
|
|
3
|
-
import { ValidationStrategyKind } from '
|
|
1
|
+
import { FormModel } from '../../../index';
|
|
2
|
+
import { ValidationSchema } from '../../../form/validation';
|
|
3
|
+
import { ValidationStrategyKind } from '../../../form/validation/strategy';
|
|
4
4
|
export interface UseFormValidationArgs<T> {
|
|
5
5
|
/** Модель данных формы. */
|
|
6
6
|
model: FormModel<T>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FieldNode } from '
|
|
2
|
-
import { ArrayNode } from '
|
|
3
|
-
import { FormValue } from '
|
|
1
|
+
import { FieldNode } from '../../../form/nodes/field-node';
|
|
2
|
+
import { ArrayNode } from '../../../form/nodes/array-node';
|
|
3
|
+
import { FormValue } from '../../../form/types/index';
|
|
4
4
|
import { FieldControlState, ArrayControlState } from './types';
|
|
5
5
|
/**
|
|
6
6
|
* React-хук для подписки на состояние {@link ArrayNode}.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FieldNode } from '
|
|
2
|
-
import { FormValue } from '
|
|
1
|
+
import { FieldNode } from '../../../form/nodes/field-node';
|
|
2
|
+
import { FormValue } from '../../../form/types/index';
|
|
3
3
|
/**
|
|
4
4
|
* React-хук для подписки только на значение поля.
|
|
5
5
|
*
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модуль `platforms/react` — биндинги ядра к React.
|
|
3
|
+
*
|
|
4
|
+
* Единственное место в пакете, где есть runtime-зависимость от `react` и
|
|
5
|
+
* `use-sync-external-store`: слои `model` и `form` от React свободны (в `form/types/` остаются
|
|
6
|
+
* только type-only `ComponentType`/`ElementType` — они стираются при компиляции). Хуки читают
|
|
7
|
+
* сигналы нод формы через `useSyncExternalStore` и отдают плоские снапшоты
|
|
8
|
+
* ({@link FieldControlState}/{@link ArrayControlState}) в рендер.
|
|
9
|
+
*
|
|
10
|
+
* Barrel заведён как точка для будущего сабпата `@reformer/core/react`; сейчас его состав
|
|
11
|
+
* реэкспортируется зонтиком `@reformer/core` — ровно теми же именами, что и до выноса.
|
|
12
|
+
*
|
|
13
|
+
* @group Platforms
|
|
14
|
+
* @module platforms/react
|
|
15
|
+
*/
|
|
16
|
+
export { useFormControl } from './hooks/useFormControl';
|
|
17
|
+
export { useFormControlValue } from './hooks/useFormControlValue';
|
|
18
|
+
export { useArrayLength } from './hooks/useArrayLength';
|
|
19
|
+
export { useFormValidation } from './hooks/use-form-validation';
|
|
20
|
+
export type { UseFormValidationArgs, UseFormValidationResult } from './hooks/use-form-validation';
|
|
21
|
+
export { useFormBundle } from './hooks/use-form-bundle';
|
|
22
|
+
export type { FormBundleLike } from './hooks/use-form-bundle';
|
|
23
|
+
export type { FieldControlState, ArrayControlState } from './hooks/types';
|
package/dist/signals.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* пакетов — это критично для проверок вида `value instanceof Signal` над сигналами,
|
|
8
8
|
* созданными внутри @reformer/core.
|
|
9
9
|
*
|
|
10
|
-
* @module
|
|
10
|
+
* @module signals
|
|
11
11
|
*/
|
|
12
12
|
export { signal, computed, effect, batch, untracked, Signal } from '@preact/signals-core';
|
|
13
13
|
export type { ReadonlySignal } from '@preact/signals-core';
|