@reformer/core 5.0.0 → 6.0.0
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 +7 -5
- package/dist/behaviors-O9a1Djj9.js +147 -0
- package/dist/behaviors.d.ts +131 -4
- package/dist/behaviors.js +298 -27
- package/dist/core/model/behaviors.d.ts +123 -0
- package/dist/core/model/form-model.d.ts +18 -0
- package/dist/core/model/index.d.ts +12 -0
- package/dist/core/model/types.d.ts +112 -0
- package/dist/core/model/validate-model.d.ts +46 -0
- package/dist/core/nodes/array-node.d.ts +34 -2
- package/dist/core/nodes/group-node.d.ts +19 -71
- package/dist/core/nodes/model-array-node.d.ts +71 -0
- package/dist/core/types/deep-schema.d.ts +16 -2
- package/dist/core/types/form-proxy.d.ts +17 -6
- package/dist/core/types/index.d.ts +5 -23
- package/dist/core/types/validation-schema.d.ts +15 -66
- package/dist/core/utils/create-form.d.ts +24 -0
- package/dist/core/utils/derived-registry.d.ts +5 -0
- package/dist/core/utils/index.d.ts +4 -4
- package/dist/core/utils/signal-node-registry.d.ts +12 -0
- package/dist/core/utils/type-guards.d.ts +2 -3
- package/dist/core/validation/index.d.ts +5 -11
- package/dist/core/validation/validators/email.d.ts +5 -29
- package/dist/core/validation/validators/future-date.d.ts +6 -28
- package/dist/core/validation/validators/index.d.ts +11 -4
- package/dist/core/validation/validators/integer.d.ts +13 -0
- package/dist/core/validation/validators/is-date.d.ts +5 -28
- package/dist/core/validation/validators/is-number.d.ts +14 -0
- package/dist/core/validation/validators/max-age.d.ts +6 -29
- package/dist/core/validation/validators/max-date.d.ts +6 -29
- package/dist/core/validation/validators/max-length.d.ts +5 -30
- package/dist/core/validation/validators/max.d.ts +7 -29
- package/dist/core/validation/validators/min-age.d.ts +6 -29
- package/dist/core/validation/validators/min-date.d.ts +6 -29
- package/dist/core/validation/validators/min-length.d.ts +5 -30
- package/dist/core/validation/validators/min.d.ts +7 -29
- package/dist/core/validation/validators/multiple-of.d.ts +13 -0
- package/dist/core/validation/validators/non-negative.d.ts +13 -0
- package/dist/core/validation/validators/non-zero.d.ts +13 -0
- package/dist/core/validation/validators/past-date.d.ts +5 -28
- package/dist/core/validation/validators/pattern.d.ts +5 -32
- package/dist/core/validation/validators/phone.d.ts +9 -19
- package/dist/core/validation/validators/required.d.ts +10 -31
- package/dist/core/validation/validators/url.d.ts +13 -15
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1299 -875
- package/dist/validators/email.js +7 -9
- package/dist/validators/future-date.js +11 -12
- package/dist/validators/integer.d.ts +2 -0
- package/dist/validators/integer.js +10 -0
- package/dist/validators/is-date.js +6 -7
- package/dist/validators/is-number.d.ts +2 -0
- package/dist/validators/is-number.js +10 -0
- package/dist/validators/max-age.js +11 -12
- package/dist/validators/max-date.js +11 -12
- package/dist/validators/max-length.js +12 -8
- package/dist/validators/max.js +5 -6
- package/dist/validators/min-age.js +10 -11
- package/dist/validators/min-date.js +12 -13
- package/dist/validators/min-length.js +12 -8
- package/dist/validators/min.js +6 -7
- package/dist/validators/multiple-of.d.ts +2 -0
- package/dist/validators/multiple-of.js +10 -0
- package/dist/validators/non-negative.d.ts +2 -0
- package/dist/validators/non-negative.js +10 -0
- package/dist/validators/non-zero.d.ts +2 -0
- package/dist/validators/non-zero.js +10 -0
- package/dist/validators/past-date.js +11 -12
- package/dist/validators/pattern.js +6 -7
- package/dist/validators/phone.js +14 -32
- package/dist/validators/required.js +9 -10
- package/dist/validators/url.js +19 -16
- package/dist/validators.js +67 -54
- package/llms.txt +959 -2584
- package/package.json +19 -35
- package/dist/behaviors/compute-from.d.ts +0 -2
- package/dist/behaviors/compute-from.js +0 -31
- package/dist/behaviors/copy-from.d.ts +0 -2
- package/dist/behaviors/copy-from.js +0 -29
- package/dist/behaviors/enable-when.d.ts +0 -2
- package/dist/behaviors/enable-when.js +0 -25
- package/dist/behaviors/reset-when.d.ts +0 -2
- package/dist/behaviors/reset-when.js +0 -24
- package/dist/behaviors/revalidate-when.d.ts +0 -2
- package/dist/behaviors/revalidate-when.js +0 -18
- package/dist/behaviors/sync-fields.d.ts +0 -2
- package/dist/behaviors/sync-fields.js +0 -41
- package/dist/behaviors/transform-value.d.ts +0 -2
- package/dist/behaviors/transform-value.js +0 -45
- package/dist/behaviors/watch-field.d.ts +0 -2
- package/dist/behaviors/watch-field.js +0 -21
- package/dist/core/behavior/behavior-context.d.ts +0 -47
- package/dist/core/behavior/behavior-registry.d.ts +0 -85
- package/dist/core/behavior/behaviors/compute-from.d.ts +0 -70
- package/dist/core/behavior/behaviors/copy-from.d.ts +0 -56
- package/dist/core/behavior/behaviors/enable-when.d.ts +0 -118
- package/dist/core/behavior/behaviors/index.d.ts +0 -11
- package/dist/core/behavior/behaviors/reset-when.d.ts +0 -64
- package/dist/core/behavior/behaviors/revalidate-when.d.ts +0 -53
- package/dist/core/behavior/behaviors/sync-fields.d.ts +0 -48
- package/dist/core/behavior/behaviors/transform-value.d.ts +0 -192
- package/dist/core/behavior/behaviors/watch-field.d.ts +0 -80
- package/dist/core/behavior/compose-behavior.d.ts +0 -96
- package/dist/core/behavior/index.d.ts +0 -11
- package/dist/core/behavior/types.d.ts +0 -146
- package/dist/core/types/field-path.d.ts +0 -42
- package/dist/core/types/form-context.d.ts +0 -89
- package/dist/core/utils/field-path-navigator.d.ts +0 -240
- package/dist/core/utils/field-path.d.ts +0 -60
- package/dist/core/utils/registry-helpers.d.ts +0 -43
- package/dist/core/validation/core/apply-when.d.ts +0 -22
- package/dist/core/validation/core/apply.d.ts +0 -52
- package/dist/core/validation/core/index.d.ts +0 -8
- package/dist/core/validation/core/validate-async.d.ts +0 -36
- package/dist/core/validation/core/validate-tree.d.ts +0 -35
- package/dist/core/validation/core/validate.d.ts +0 -26
- package/dist/core/validation/validate-form.d.ts +0 -48
- package/dist/core/validation/validation-applicator.d.ts +0 -70
- package/dist/core/validation/validation-context.d.ts +0 -86
- package/dist/core/validation/validation-registry.d.ts +0 -142
- package/dist/core/validation/validators/array-validators.d.ts +0 -53
- package/dist/core/validation/validators/number.d.ts +0 -31
- package/dist/field-path-DuKdGcIE.js +0 -66
- package/dist/hooks/useHiddenCondition.d.ts +0 -25
- package/dist/index-D25LsbRm.js +0 -73
- package/dist/registry-helpers-Bv_BJ1s-.js +0 -615
- package/dist/safe-effect-Dh8uw81c.js +0 -20
- package/dist/validate-C3XiA_zf.js +0 -10
- package/dist/validators/number.d.ts +0 -2
- package/dist/validators/number.js +0 -35
- package/dist/validators-BGsNOgT1.js +0 -207
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
validate,
|
|
41
41
|
watchField,
|
|
42
42
|
FieldNode,
|
|
43
|
+
type ValidationSchemaFn,
|
|
44
|
+
type BehaviorSchemaFn,
|
|
43
45
|
} from '@reformer/core';
|
|
44
46
|
|
|
45
47
|
// 0. Simple FormField component
|
|
@@ -76,7 +78,7 @@ const formSchema = {
|
|
|
76
78
|
};
|
|
77
79
|
|
|
78
80
|
// 3. Validation schema
|
|
79
|
-
validationSchema = (path) => {
|
|
81
|
+
const validationSchema: ValidationSchemaFn<RegistrationForm> = (path) => {
|
|
80
82
|
required(path.username);
|
|
81
83
|
|
|
82
84
|
required(path.email);
|
|
@@ -85,9 +87,9 @@ validationSchema = (path) => {
|
|
|
85
87
|
required(path.password);
|
|
86
88
|
required(path.confirmPassword);
|
|
87
89
|
|
|
88
|
-
// Cross-field validation:
|
|
89
|
-
validate(path.confirmPassword, (value,
|
|
90
|
-
const password =
|
|
90
|
+
// Cross-field validation: вешается на поле-носитель ошибки, соседнее поле читается через `root`
|
|
91
|
+
validate(path.confirmPassword, (value, _control, root) => {
|
|
92
|
+
const password = root.password.value.value;
|
|
91
93
|
if (value && password && value !== password) {
|
|
92
94
|
return { code: 'mismatch', message: 'Passwords do not match' };
|
|
93
95
|
}
|
|
@@ -96,7 +98,7 @@ validationSchema = (path) => {
|
|
|
96
98
|
};
|
|
97
99
|
|
|
98
100
|
// 4. Behavior schema
|
|
99
|
-
|
|
101
|
+
const behaviorSchema: BehaviorSchemaFn<RegistrationForm> = (path) => {
|
|
100
102
|
// Clear confirmPassword when password changes (if not empty)
|
|
101
103
|
watchField(path.password, (_, ctx) => {
|
|
102
104
|
const confirmValue = ctx.form.confirmPassword.value.value;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { effect as u } from "@preact/signals-core";
|
|
2
|
+
const o = /* @__PURE__ */ new WeakSet();
|
|
3
|
+
function h(e) {
|
|
4
|
+
o.add(e);
|
|
5
|
+
}
|
|
6
|
+
function p(e) {
|
|
7
|
+
return o.has(e);
|
|
8
|
+
}
|
|
9
|
+
const d = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
function y(e, n) {
|
|
11
|
+
d.set(e, n);
|
|
12
|
+
}
|
|
13
|
+
function v(e) {
|
|
14
|
+
return d.get(e);
|
|
15
|
+
}
|
|
16
|
+
function b(e) {
|
|
17
|
+
return (...n) => {
|
|
18
|
+
queueMicrotask(() => e(...n));
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function s(e) {
|
|
22
|
+
queueMicrotask(e);
|
|
23
|
+
}
|
|
24
|
+
function w(e, n) {
|
|
25
|
+
return () => {
|
|
26
|
+
n(() => {
|
|
27
|
+
queueMicrotask(e);
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function W(e, n, r, a) {
|
|
32
|
+
return u(() => {
|
|
33
|
+
const t = e.map((f) => f.value);
|
|
34
|
+
if (a?.when && !a.when(...t)) return;
|
|
35
|
+
const l = r(...t);
|
|
36
|
+
n.peek() !== l && (n.value = l);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function x(e, n, r) {
|
|
40
|
+
return u(() => {
|
|
41
|
+
const a = e.value;
|
|
42
|
+
if (r?.when && !r.when()) return;
|
|
43
|
+
const t = r?.transform ? r.transform(a) : a;
|
|
44
|
+
n.peek() !== t && (n.value = t);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function F(e, n, r) {
|
|
48
|
+
let a = !0;
|
|
49
|
+
return u(() => {
|
|
50
|
+
const t = e.value;
|
|
51
|
+
a && (a = !1, !r?.immediate) || n(t);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function k(e, n, r) {
|
|
55
|
+
return u(() => {
|
|
56
|
+
const a = n(), t = v(e);
|
|
57
|
+
t && s(() => {
|
|
58
|
+
a ? t.enable() : (t.disable(), r?.resetOnDisable && t.reset());
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function g(e, n, r) {
|
|
63
|
+
return k(e, () => !n(), r);
|
|
64
|
+
}
|
|
65
|
+
function D(e, n) {
|
|
66
|
+
let r = !1;
|
|
67
|
+
return u(() => {
|
|
68
|
+
const a = e.value;
|
|
69
|
+
r || s(() => {
|
|
70
|
+
const t = n(a);
|
|
71
|
+
if (e.peek() !== t) {
|
|
72
|
+
r = !0;
|
|
73
|
+
try {
|
|
74
|
+
e.value = t;
|
|
75
|
+
} finally {
|
|
76
|
+
r = !1;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function M(e, n, r) {
|
|
83
|
+
const a = r?.resetValue ?? null;
|
|
84
|
+
return u(() => {
|
|
85
|
+
const t = n();
|
|
86
|
+
s(() => {
|
|
87
|
+
t && e.peek() !== a && (e.value = a);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function q(e, n, r) {
|
|
92
|
+
const a = r?.transform;
|
|
93
|
+
let t = !1;
|
|
94
|
+
const l = u(() => {
|
|
95
|
+
const i = e.value;
|
|
96
|
+
t || s(() => {
|
|
97
|
+
t = !0;
|
|
98
|
+
try {
|
|
99
|
+
const c = a ? a(i) : i;
|
|
100
|
+
n.peek() !== c && (n.value = c);
|
|
101
|
+
} finally {
|
|
102
|
+
t = !1;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}), f = u(() => {
|
|
106
|
+
const i = n.value;
|
|
107
|
+
t || s(() => {
|
|
108
|
+
t = !0;
|
|
109
|
+
try {
|
|
110
|
+
e.peek() !== i && (e.value = i);
|
|
111
|
+
} finally {
|
|
112
|
+
t = !1;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
return () => {
|
|
117
|
+
l(), f();
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function S(e, n) {
|
|
121
|
+
let r = !0;
|
|
122
|
+
return u(() => {
|
|
123
|
+
if (e.forEach((a) => a.value), r) {
|
|
124
|
+
r = !1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
s(() => n());
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
M as a,
|
|
132
|
+
S as b,
|
|
133
|
+
x as c,
|
|
134
|
+
y as d,
|
|
135
|
+
k as e,
|
|
136
|
+
b as f,
|
|
137
|
+
v as g,
|
|
138
|
+
w as h,
|
|
139
|
+
p as i,
|
|
140
|
+
W as j,
|
|
141
|
+
g as k,
|
|
142
|
+
h as m,
|
|
143
|
+
s as r,
|
|
144
|
+
q as s,
|
|
145
|
+
D as t,
|
|
146
|
+
F as w
|
|
147
|
+
};
|
package/dist/behaviors.d.ts
CHANGED
|
@@ -1,6 +1,133 @@
|
|
|
1
|
+
import { Signal, ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { BehaviorCleanup, FormModel, FormProxy } from './index';
|
|
3
|
+
/** Контекст схемы поведения: модель (значения/сигналы) + форма (ноды). */
|
|
4
|
+
export type BehaviorScope<T> = {
|
|
5
|
+
model: FormModel<T>;
|
|
6
|
+
form: FormProxy<T>;
|
|
7
|
+
};
|
|
8
|
+
/** Результат {@link defineFormBehavior}; передаётся в `createForm({ behavior })`. */
|
|
9
|
+
export interface FormBehavior<T> {
|
|
10
|
+
/** @internal Запускается `createForm` после построения нод и заполнения реестра сигнал→нода. */
|
|
11
|
+
__run(model: FormModel<T>, form: FormProxy<T>): BehaviorCleanup;
|
|
12
|
+
}
|
|
13
|
+
export type { Signal, ReadonlySignal, BehaviorCleanup };
|
|
14
|
+
/** Зарегистрировать произвольную отписку в активной схеме. */
|
|
15
|
+
export declare function onDispose(cleanup: BehaviorCleanup): void;
|
|
16
|
+
/** Текущий scope ({ model, form }) активной схемы (escape hatch для кросс-операторов). */
|
|
17
|
+
export declare function getScope<T>(): BehaviorScope<T>;
|
|
1
18
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
19
|
+
* Описать поведение формы декларативно. Возвращает {@link FormBehavior} для `createForm({ behavior })`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* export const myBehavior = defineFormBehavior<MyForm>(({ model, form }) => {
|
|
24
|
+
* compute(model.$.total, () => model.price * model.qty);
|
|
25
|
+
* enableWhen([model.$.city], () => Boolean(model.country));
|
|
26
|
+
* onChange(model.$.country, async (c) => form.city.updateComponentProps({ options: await load(c) }));
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
4
29
|
*/
|
|
5
|
-
export
|
|
6
|
-
|
|
30
|
+
export declare function defineFormBehavior<T>(setup: (scope: BehaviorScope<T>) => void): FormBehavior<T>;
|
|
31
|
+
/** Реактивный эффект (авто-dispose). Колбэк может вернуть собственный cleanup. */
|
|
32
|
+
export declare function effect(fn: () => void | (() => void)): void;
|
|
33
|
+
/** Отложенная запись вне effect-контекста (микротаск) — защита от «Cycle detected». */
|
|
34
|
+
export declare function defer(fn: () => void): void;
|
|
35
|
+
/** Вычисляемое поле с auto-tracking: `target = read()` при изменении прочитанных сигналов. */
|
|
36
|
+
export declare function compute<R>(target: Signal<R>, read: () => R, options?: {
|
|
37
|
+
when?: () => boolean;
|
|
38
|
+
}): void;
|
|
39
|
+
/** Вычисляемое поле с явным списком зависимостей (escape hatch). */
|
|
40
|
+
export declare function computeFrom<R>(sources: ReadonlySignal<unknown>[], target: Signal<R>, fn: (...values: any[]) => R, options?: {
|
|
41
|
+
when?: (...values: any[]) => boolean;
|
|
42
|
+
}): void;
|
|
43
|
+
/** Копирование `source → target` — скаляр или группа (объект целиком). */
|
|
44
|
+
export declare function copyFrom<T>(source: ReadonlySignal<T> | object, target: Signal<T> | object, options?: {
|
|
45
|
+
when?: () => boolean;
|
|
46
|
+
transform?: (value: T) => T;
|
|
47
|
+
}): void;
|
|
48
|
+
/** Контекст async-реакции: `signal` аннулируется, когда поле меняется снова до завершения колбэка. */
|
|
49
|
+
export interface ChangeContext {
|
|
50
|
+
signal: AbortSignal;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Реакция на изменение поля; `{ debounce, immediate }`.
|
|
54
|
+
*
|
|
55
|
+
* Колбэк выполняется ВНЕ effect-контекста (микротаск/таймер) — можно безопасно писать сигналы и ноды
|
|
56
|
+
* (`updateComponentProps`/`reset`/`clear`) без ручного `defer` и без «Cycle detected».
|
|
57
|
+
*
|
|
58
|
+
* Для async-колбэков 2-м аргументом приходит `{ signal }` (AbortSignal): при следующей смене значения
|
|
59
|
+
* предыдущий `signal` аннулируется. Передавай его в `fetch` (сетевая отмена) или проверяй
|
|
60
|
+
* `signal.aborted` перед применением результата — это убирает гонки устаревших ответов (F2).
|
|
61
|
+
*/
|
|
62
|
+
export declare function onChange<T>(source: ReadonlySignal<T>, cb: (value: T, ctx: ChangeContext) => void, options?: {
|
|
63
|
+
immediate?: boolean;
|
|
64
|
+
debounce?: number;
|
|
65
|
+
}): void;
|
|
66
|
+
type EnableTarget = ReadonlySignal<unknown> | object;
|
|
67
|
+
/** Условное включение поля(ей) — скаляр, массив целей или группа. `resetOnDisable=false` по умолчанию. */
|
|
68
|
+
export declare function enableWhen(target: EnableTarget | EnableTarget[], condition: () => boolean, options?: {
|
|
69
|
+
resetOnDisable?: boolean;
|
|
70
|
+
}): void;
|
|
71
|
+
/** Условное выключение поля(ей) (инверсия {@link enableWhen}). */
|
|
72
|
+
export declare function disableWhen(target: EnableTarget | EnableTarget[], condition: () => boolean, options?: {
|
|
73
|
+
resetOnDisable?: boolean;
|
|
74
|
+
}): void;
|
|
75
|
+
/** Трансформация значения поля (идемпотентная). */
|
|
76
|
+
export declare function transformValue<T>(target: Signal<T>, transformer: (value: T) => T): void;
|
|
77
|
+
/** Сброс значения по условию. */
|
|
78
|
+
export declare function resetWhen<T>(target: Signal<T>, condition: () => boolean, options?: {
|
|
79
|
+
resetValue?: T;
|
|
80
|
+
}): void;
|
|
81
|
+
/** Двусторонняя синхронизация двух полей. */
|
|
82
|
+
export declare function syncFields<T>(a: Signal<T>, b: Signal<T>, options?: {
|
|
83
|
+
transform?: (value: T) => T;
|
|
84
|
+
}): void;
|
|
85
|
+
/** Ревалидация при изменении зависимостей. */
|
|
86
|
+
export declare function revalidateWhen(deps: ReadonlySignal<unknown>[], revalidate: () => void): void;
|
|
87
|
+
/**
|
|
88
|
+
* Применить под-схему к КАЖДОМУ элементу динамического массива (per-item поведение).
|
|
89
|
+
* Реагирует на добавление/удаление строк: новым строкам поведение применяется, удалённым — отписывается.
|
|
90
|
+
*
|
|
91
|
+
* Под-схема получает scope строки: `model` (под-модель строки — `row.$.field`) и `form` (нода строки).
|
|
92
|
+
* - Value-операции (`compute`/`copyFrom`/`transformValue` на `row.$.*`) работают всегда.
|
|
93
|
+
* - Node-операции (`enableWhen`/`updateComponentProps`/`reset` через `form.*`) требуют, чтобы массив был
|
|
94
|
+
* МАТЕРИАЛИЗОВАН в форме (узел `{ array, item }` в схеме) — тогда `form` строки = та же нода, что
|
|
95
|
+
* рендерится, а её сигналы зарегистрированы (`enableWhen` резолвит ноду). Без материализации доступ
|
|
96
|
+
* к `form.*` бросит понятную ошибку (см. {@link unmaterializedRowForm}).
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* applyEach(model.$.items, defineFormBehavior<Item>(({ model: row, form }) => {
|
|
100
|
+
* compute(row.$.lineTotal, () => row.qty * row.price); // value-op — всегда
|
|
101
|
+
* enableWhen(row.$.discount, () => row.qty > 10); // node-op — нужна материализация массива
|
|
102
|
+
* }));
|
|
103
|
+
*/
|
|
104
|
+
export declare function applyEach<TItem>(array: object, itemSchema: FormBehavior<TItem>): void;
|
|
105
|
+
/**
|
|
106
|
+
* Взаимное исключение булева флага среди строк массива (single-selection — «единственный primary»).
|
|
107
|
+
* Когда флаг строки становится true, у остальных строк он сбрасывается в false. Не хрупок к push:
|
|
108
|
+
* новые строки с флагом false исключения не запускают.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* exclusiveFlag(model.$.contacts, (row) => row.$.primary);
|
|
112
|
+
*/
|
|
113
|
+
export declare function exclusiveFlag<TItem>(array: object, getFlag: (row: FormModel<TItem>) => Signal<boolean>): void;
|
|
114
|
+
/**
|
|
115
|
+
* Агрегатная запись в строки массива. `derive(snapshot)` получает СНИМОК строк и возвращает список
|
|
116
|
+
* `{ index, patch }`, который применяется к строкам. Записи КОАЛЕСИРУЮТСЯ в один отложенный проход на
|
|
117
|
+
* финальном состоянии — поэтому массовые синхронные мутации (push в цикле) не вызывают каскад на
|
|
118
|
+
* промежуточных состояниях. `derive` должна сходиться (на фикспоинте возвращать те же значения).
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // последняя строка = 100 − Σ(остальные)
|
|
122
|
+
* aggregateInto(model.$.rows, (rows) => {
|
|
123
|
+
* const n = rows.length; if (n === 0) return [];
|
|
124
|
+
* const others = rows.slice(0, n - 1).reduce((s, r) => s + r.percent, 0);
|
|
125
|
+
* return [{ index: n - 1, patch: { percent: 100 - others } }];
|
|
126
|
+
* });
|
|
127
|
+
*/
|
|
128
|
+
export declare function aggregateInto<TItem>(array: object, derive: (rows: TItem[]) => Array<{
|
|
129
|
+
index: number;
|
|
130
|
+
patch: Partial<TItem>;
|
|
131
|
+
}>): void;
|
|
132
|
+
/** Применить под-схему к одному или нескольким полям-группам (переиспользование). */
|
|
133
|
+
export declare function apply<TField>(targets: object | object[], subSchema: FormBehavior<TField>): void;
|
package/dist/behaviors.js
CHANGED
|
@@ -1,29 +1,300 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as s,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { effect as j } from "@preact/signals-core";
|
|
2
|
+
import { r as $, m as x, c as O, w as F, e as S, t as A, a as C, s as M, b as D } from "./behaviors-O9a1Djj9.js";
|
|
3
|
+
let p = null;
|
|
4
|
+
function k(e) {
|
|
5
|
+
if (!p)
|
|
6
|
+
throw new Error(
|
|
7
|
+
`[@reformer/core/behaviors] "${e}" вызван вне defineFormBehavior(...) — операторы поведения можно вызывать только внутри схемы.`
|
|
8
|
+
);
|
|
9
|
+
return p;
|
|
10
|
+
}
|
|
11
|
+
function a(e) {
|
|
12
|
+
k("onDispose").cleanups.push(e);
|
|
13
|
+
}
|
|
14
|
+
function y() {
|
|
15
|
+
const e = k("getScope");
|
|
16
|
+
return { model: e.model, form: e.form };
|
|
17
|
+
}
|
|
18
|
+
function G(e) {
|
|
19
|
+
return {
|
|
20
|
+
__run(n, t) {
|
|
21
|
+
const o = { cleanups: [], model: n, form: t }, r = p;
|
|
22
|
+
p = o;
|
|
23
|
+
try {
|
|
24
|
+
e({ model: n, form: t });
|
|
25
|
+
} finally {
|
|
26
|
+
p = r;
|
|
27
|
+
}
|
|
28
|
+
return () => {
|
|
29
|
+
for (const c of o.cleanups) c();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function m(e) {
|
|
35
|
+
a(j(e));
|
|
36
|
+
}
|
|
37
|
+
function g(e) {
|
|
38
|
+
$(e);
|
|
39
|
+
}
|
|
40
|
+
const h = (e) => typeof e == "object" && e !== null && typeof e.peek == "function", P = (e) => Array.isArray(e) ? e : [e];
|
|
41
|
+
function B(e) {
|
|
42
|
+
const n = {};
|
|
43
|
+
for (const t of Object.keys(e)) {
|
|
44
|
+
const o = e[t];
|
|
45
|
+
n[t] = h(o) ? o.value : B(o);
|
|
46
|
+
}
|
|
47
|
+
return n;
|
|
48
|
+
}
|
|
49
|
+
function E(e, n) {
|
|
50
|
+
for (const t of Object.keys(e)) {
|
|
51
|
+
const o = e[t];
|
|
52
|
+
h(o) ? o.value = n?.[t] : E(o, n?.[t] ?? {});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function T(e) {
|
|
56
|
+
if (!e) return;
|
|
57
|
+
const { form: n } = y();
|
|
58
|
+
return n.getFieldByPath(e);
|
|
59
|
+
}
|
|
60
|
+
function W(e) {
|
|
61
|
+
return new Proxy(
|
|
62
|
+
{},
|
|
63
|
+
{
|
|
64
|
+
get: (n, t) => {
|
|
65
|
+
if (t === "$") return e;
|
|
66
|
+
if (typeof t != "string") return;
|
|
67
|
+
const o = e[t];
|
|
68
|
+
if (o != null)
|
|
69
|
+
return h(o) ? o.value : W(o);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function V(e) {
|
|
75
|
+
return (n) => {
|
|
76
|
+
try {
|
|
77
|
+
n();
|
|
78
|
+
} catch (t) {
|
|
79
|
+
if (t instanceof Error && /cycle detected/i.test(t.message)) {
|
|
80
|
+
const o = e.__path ?? "?";
|
|
81
|
+
throw new Error(
|
|
82
|
+
`[@reformer/core/behaviors] compute("${o}"): расходящийся цикл пересчёта — взаимные compute/computeFrom без стабилизации. Проверьте зависимости или добавьте стабилизирующее условие (when) / разорвите цикл через peek.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
throw t;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function K(e, n, t) {
|
|
90
|
+
x(e);
|
|
91
|
+
const o = V(e);
|
|
92
|
+
m(() => {
|
|
93
|
+
if (t?.when && !t.when()) return;
|
|
94
|
+
const r = n();
|
|
95
|
+
e.peek() !== r && o(() => {
|
|
96
|
+
e.value = r;
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function L(e, n, t, o) {
|
|
101
|
+
x(n);
|
|
102
|
+
const r = V(n);
|
|
103
|
+
m(() => {
|
|
104
|
+
const c = e.map((f) => f.value);
|
|
105
|
+
if (o?.when && !o.when(...c)) return;
|
|
106
|
+
const i = t(...c);
|
|
107
|
+
n.peek() !== i && r(() => {
|
|
108
|
+
n.value = i;
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function Q(e, n, t) {
|
|
113
|
+
if (h(e) && h(n)) {
|
|
114
|
+
a(O(e, n, t));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const o = e, r = n;
|
|
118
|
+
m(() => {
|
|
119
|
+
const c = B(o);
|
|
120
|
+
t?.when && !t.when() || g(() => E(r, c));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function I(e, n, t) {
|
|
124
|
+
let o;
|
|
125
|
+
const r = (c) => {
|
|
126
|
+
o?.abort(), o = new AbortController();
|
|
127
|
+
const { signal: i } = o;
|
|
128
|
+
$(() => n(c, { signal: i }));
|
|
129
|
+
};
|
|
130
|
+
if (!t?.debounce)
|
|
131
|
+
a(F(e, r, { immediate: t?.immediate }));
|
|
132
|
+
else {
|
|
133
|
+
let c;
|
|
134
|
+
const i = F(
|
|
135
|
+
e,
|
|
136
|
+
(f) => {
|
|
137
|
+
c && clearTimeout(c), c = setTimeout(() => r(f), t.debounce);
|
|
138
|
+
},
|
|
139
|
+
{ immediate: t?.immediate }
|
|
140
|
+
);
|
|
141
|
+
a(() => {
|
|
142
|
+
c && clearTimeout(c), i();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
a(() => o?.abort());
|
|
146
|
+
}
|
|
147
|
+
function R(e, n, t) {
|
|
148
|
+
for (const o of P(e))
|
|
149
|
+
h(o) ? a(S(o, n, t)) : q(o, n, t);
|
|
150
|
+
}
|
|
151
|
+
function U(e, n, t) {
|
|
152
|
+
R(e, () => !n(), t);
|
|
153
|
+
}
|
|
154
|
+
function q(e, n, t) {
|
|
155
|
+
const o = T(e.__path);
|
|
156
|
+
o && m(() => {
|
|
157
|
+
const r = n();
|
|
158
|
+
g(() => {
|
|
159
|
+
r ? o.enable() : (o.disable(), t?.resetOnDisable && o.reset());
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function X(e, n) {
|
|
164
|
+
a(A(e, n));
|
|
165
|
+
}
|
|
166
|
+
function Y(e, n, t) {
|
|
167
|
+
a(C(e, n, t));
|
|
168
|
+
}
|
|
169
|
+
function Z(e, n, t) {
|
|
170
|
+
a(M(e, n, t));
|
|
171
|
+
}
|
|
172
|
+
function ee(e, n) {
|
|
173
|
+
a(D(e, n));
|
|
174
|
+
}
|
|
175
|
+
function _(e, n) {
|
|
176
|
+
return n.split(".").reduce((t, o) => t?.[o], e);
|
|
177
|
+
}
|
|
178
|
+
function z(e) {
|
|
179
|
+
return new Proxy(
|
|
180
|
+
{},
|
|
181
|
+
{
|
|
182
|
+
get(n, t) {
|
|
183
|
+
if (!(typeof t == "symbol" || t === "then"))
|
|
184
|
+
throw new Error(
|
|
185
|
+
`[@reformer/core/behaviors] applyEach: форма строки массива "${e}" недоступна (form.${String(
|
|
186
|
+
t
|
|
187
|
+
)}) — массив не материализован в схеме формы. Per-row node-операции (enableWhen/updateComponentProps/reset) требуют узла { array, item } в схеме; value-операции (row.$.*: compute/copyFrom/transformValue) работают и без материализации.`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function N(e, n) {
|
|
194
|
+
const { model: t, form: o } = y(), r = e.__path;
|
|
195
|
+
if (!r) return;
|
|
196
|
+
const c = _(t, r);
|
|
197
|
+
if (!c) return;
|
|
198
|
+
const i = o.getFieldByPath(
|
|
199
|
+
r
|
|
200
|
+
), f = i?.length, s = /* @__PURE__ */ new Map();
|
|
201
|
+
m(() => {
|
|
202
|
+
const d = f ? f.value : c.length, w = /* @__PURE__ */ new Set();
|
|
203
|
+
for (let u = 0; u < d; u++) {
|
|
204
|
+
const l = c.at(u);
|
|
205
|
+
if (l != null && (w.add(l), !s.has(l))) {
|
|
206
|
+
const b = i?.at?.(u) ?? z(r);
|
|
207
|
+
s.set(l, n.__run(l, b));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (const [u, l] of s)
|
|
211
|
+
w.has(u) || (l(), s.delete(u));
|
|
212
|
+
}), a(() => {
|
|
213
|
+
for (const d of s.values()) d();
|
|
214
|
+
s.clear();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function v(e) {
|
|
218
|
+
if (e == null || typeof e != "object") return;
|
|
219
|
+
const n = e;
|
|
220
|
+
if (typeof n.at == "function" && typeof n.length == "number") {
|
|
221
|
+
const t = n.length;
|
|
222
|
+
for (let o = 0; o < t; o++) v(n.at(o));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
for (const t of Object.keys(e)) v(e[t]);
|
|
226
|
+
}
|
|
227
|
+
function te(e, n) {
|
|
228
|
+
const { model: t } = y(), o = e.__path;
|
|
229
|
+
if (!o) return;
|
|
230
|
+
const r = _(t, o);
|
|
231
|
+
r && N(
|
|
232
|
+
e,
|
|
233
|
+
G(({ model: c }) => {
|
|
234
|
+
I(n(c), (i) => {
|
|
235
|
+
if (i)
|
|
236
|
+
for (let f = 0; f < r.length; f++) {
|
|
237
|
+
const s = r.at(f);
|
|
238
|
+
if (s && s !== c) {
|
|
239
|
+
const d = n(s);
|
|
240
|
+
d.peek() && (d.value = !1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
function ne(e, n) {
|
|
248
|
+
const { model: t } = y(), o = e.__path;
|
|
249
|
+
if (!o) return;
|
|
250
|
+
const r = _(t, o);
|
|
251
|
+
if (!r) return;
|
|
252
|
+
let c = !1, i = 0;
|
|
253
|
+
m(() => {
|
|
254
|
+
v(r), !c && (c = !0, g(() => {
|
|
255
|
+
c = !1;
|
|
256
|
+
const f = n(r.toArray());
|
|
257
|
+
let s = !1;
|
|
258
|
+
for (const { index: d, patch: w } of f) {
|
|
259
|
+
const u = r.at(d);
|
|
260
|
+
if (u)
|
|
261
|
+
for (const [l, b] of Object.entries(w))
|
|
262
|
+
u[l] !== b && (u[l] = b, s = !0);
|
|
263
|
+
}
|
|
264
|
+
if (i = s ? i + 1 : 0, i > 50)
|
|
265
|
+
throw i = 0, new Error(
|
|
266
|
+
`[@reformer/core/behaviors] aggregateInto("${o}"): запись не сходится (>50 проходов) — derive должна быть идемпотентной на фикспоинте.`
|
|
267
|
+
);
|
|
268
|
+
}));
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function oe(e, n) {
|
|
272
|
+
const { form: t } = y();
|
|
273
|
+
for (const o of P(e)) {
|
|
274
|
+
const r = o, c = r.__path;
|
|
275
|
+
if (!c) continue;
|
|
276
|
+
const i = t ? t.getFieldByPath(c) : void 0, f = i ? i.getProxy?.() ?? i : void 0;
|
|
277
|
+
a(n.__run(W(r), f));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
11
280
|
export {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
281
|
+
ne as aggregateInto,
|
|
282
|
+
oe as apply,
|
|
283
|
+
N as applyEach,
|
|
284
|
+
K as compute,
|
|
285
|
+
L as computeFrom,
|
|
286
|
+
Q as copyFrom,
|
|
287
|
+
g as defer,
|
|
288
|
+
G as defineFormBehavior,
|
|
289
|
+
U as disableWhen,
|
|
290
|
+
m as effect,
|
|
291
|
+
R as enableWhen,
|
|
292
|
+
te as exclusiveFlag,
|
|
293
|
+
y as getScope,
|
|
294
|
+
I as onChange,
|
|
295
|
+
a as onDispose,
|
|
296
|
+
Y as resetWhen,
|
|
297
|
+
ee as revalidateWhen,
|
|
298
|
+
Z as syncFields,
|
|
299
|
+
X as transformValue
|
|
29
300
|
};
|