@reformer/core 8.0.0 → 9.0.0-beta.2
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 +52 -57
- package/dist/behaviors-Br4Im38V.js +263 -0
- package/dist/behaviors.js +146 -146
- package/dist/core/factories/node-factory.d.ts +5 -4
- package/dist/core/model/behaviors.d.ts +15 -0
- package/dist/core/model/types.d.ts +41 -12
- package/dist/core/model/validate-model.d.ts +8 -4
- package/dist/core/nodes/field-node.d.ts +2 -2
- package/dist/core/nodes/group-node.d.ts +39 -3
- package/dist/core/nodes/model-array-node.d.ts +13 -1
- package/dist/core/types/deep-schema.d.ts +39 -5
- package/dist/core/types/form-proxy.d.ts +27 -2
- package/dist/core/types/index.d.ts +4 -10
- package/dist/core/types/schema-node.d.ts +79 -0
- package/dist/core/types/validation-schema.d.ts +37 -16
- package/dist/core/utils/create-form.d.ts +4 -3
- package/dist/core/utils/derived-registry.d.ts +29 -0
- package/dist/core/utils/error-handler.d.ts +4 -1
- package/dist/core/utils/form-submitter.d.ts +8 -4
- package/dist/core/utils/index.d.ts +1 -1
- package/dist/core/utils/safe-effect.d.ts +27 -7
- package/dist/core/utils/type-guards.d.ts +3 -3
- package/dist/core/validation/validators/date-utils.d.ts +5 -1
- package/dist/core/validation/validators/integer.d.ts +1 -1
- package/dist/core/validation/validators/is-number.d.ts +1 -1
- package/dist/core/validation/validators/max.d.ts +1 -1
- package/dist/core/validation/validators/min.d.ts +1 -1
- package/dist/core/validation/validators/multiple-of.d.ts +1 -1
- package/dist/core/validation/validators/non-negative.d.ts +1 -1
- package/dist/core/validation/validators/non-zero.d.ts +1 -1
- package/dist/core/validation/validators/required.d.ts +2 -1
- package/dist/{date-utils-xUWFslTj.js → date-utils-dTT_x_Be.js} +12 -10
- package/dist/hooks/types.d.ts +31 -1
- package/dist/hooks/useArrayLength.d.ts +1 -2
- package/dist/hooks/useFormControl.d.ts +2 -2
- package/dist/hooks/useSignalSubscription.d.ts +8 -1
- package/dist/index.js +625 -617
- package/dist/signals.d.ts +13 -0
- package/dist/signals.js +9 -0
- package/dist/validators/future-date.js +1 -1
- package/dist/validators/is-date.js +1 -1
- package/dist/validators/max-age.js +1 -1
- package/dist/validators/max-date.js +1 -1
- package/dist/validators/min-age.js +1 -1
- package/dist/validators/min-date.js +1 -1
- package/dist/validators/multiple-of.js +11 -7
- package/dist/validators/past-date.js +1 -1
- package/dist/validators/pattern.js +7 -6
- package/dist/validators/required.js +5 -9
- package/llms.txt +715 -175
- package/package.json +6 -2
- package/dist/behaviors-O9a1Djj9.js +0 -147
package/dist/behaviors.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { effect as
|
|
2
|
-
import { r as $, m as x, c as
|
|
1
|
+
import { effect as O } from "@preact/signals-core";
|
|
2
|
+
import { r as $, m as k, u as x, c as S, w as F, e as A, t as C, a as D, s as M, b as G } from "./behaviors-Br4Im38V.js";
|
|
3
3
|
let p = null;
|
|
4
|
-
function
|
|
4
|
+
function P(e) {
|
|
5
5
|
if (!p)
|
|
6
6
|
throw new Error(
|
|
7
7
|
`[@reformer/core/behaviors] "${e}" вызван вне defineFormBehavior(...) — операторы поведения можно вызывать только внутри схемы.`
|
|
8
8
|
);
|
|
9
9
|
return p;
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
|
|
11
|
+
function s(e) {
|
|
12
|
+
P("onDispose").cleanups.push(e);
|
|
13
13
|
}
|
|
14
14
|
function y() {
|
|
15
|
-
const e =
|
|
15
|
+
const e = P("getScope");
|
|
16
16
|
return { model: e.model, form: e.form };
|
|
17
17
|
}
|
|
18
|
-
function
|
|
18
|
+
function T(e) {
|
|
19
19
|
return {
|
|
20
|
-
__run(
|
|
21
|
-
const o = { cleanups: [], model:
|
|
20
|
+
__run(t, n) {
|
|
21
|
+
const o = { cleanups: [], model: t, form: n }, r = p;
|
|
22
22
|
p = o;
|
|
23
23
|
try {
|
|
24
|
-
e({ model:
|
|
24
|
+
e({ model: t, form: n });
|
|
25
25
|
} finally {
|
|
26
26
|
p = r;
|
|
27
27
|
}
|
|
@@ -32,211 +32,211 @@ function G(e) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
function m(e) {
|
|
35
|
-
|
|
35
|
+
s(O(e));
|
|
36
36
|
}
|
|
37
|
-
function
|
|
37
|
+
function _(e) {
|
|
38
38
|
$(e);
|
|
39
39
|
}
|
|
40
|
-
const h = (e) => typeof e == "object" && e !== null && typeof e.peek == "function",
|
|
41
|
-
function
|
|
42
|
-
const
|
|
43
|
-
for (const
|
|
44
|
-
const o = e[
|
|
45
|
-
n
|
|
40
|
+
const h = (e) => typeof e == "object" && e !== null && typeof e.peek == "function", B = (e) => Array.isArray(e) ? e : [e];
|
|
41
|
+
function E(e) {
|
|
42
|
+
const t = {};
|
|
43
|
+
for (const n of Object.keys(e)) {
|
|
44
|
+
const o = e[n];
|
|
45
|
+
t[n] = h(o) ? o.value : E(o);
|
|
46
46
|
}
|
|
47
|
-
return
|
|
47
|
+
return t;
|
|
48
48
|
}
|
|
49
|
-
function
|
|
50
|
-
for (const
|
|
51
|
-
const o = e[
|
|
52
|
-
h(o) ? o.value =
|
|
49
|
+
function W(e, t) {
|
|
50
|
+
for (const n of Object.keys(e)) {
|
|
51
|
+
const o = e[n];
|
|
52
|
+
h(o) ? o.value = t?.[n] : W(o, t?.[n] ?? {});
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function I(e) {
|
|
56
56
|
if (!e) return;
|
|
57
|
-
const { form:
|
|
58
|
-
return
|
|
57
|
+
const { form: t } = y();
|
|
58
|
+
return t.getFieldByPath(e);
|
|
59
59
|
}
|
|
60
|
-
function
|
|
60
|
+
function V(e) {
|
|
61
61
|
return new Proxy(
|
|
62
62
|
{},
|
|
63
63
|
{
|
|
64
|
-
get: (
|
|
65
|
-
if (
|
|
66
|
-
if (typeof
|
|
67
|
-
const o = e[
|
|
64
|
+
get: (t, n) => {
|
|
65
|
+
if (n === "$") return e;
|
|
66
|
+
if (typeof n != "string") return;
|
|
67
|
+
const o = e[n];
|
|
68
68
|
if (o != null)
|
|
69
|
-
return h(o) ? o.value :
|
|
69
|
+
return h(o) ? o.value : V(o);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
|
-
function
|
|
75
|
-
return (
|
|
74
|
+
function j(e) {
|
|
75
|
+
return (t) => {
|
|
76
76
|
try {
|
|
77
|
-
|
|
78
|
-
} catch (
|
|
79
|
-
if (
|
|
77
|
+
t();
|
|
78
|
+
} catch (n) {
|
|
79
|
+
if (n instanceof Error && /cycle detected/i.test(n.message)) {
|
|
80
80
|
const o = e.__path ?? "?";
|
|
81
81
|
throw new Error(
|
|
82
82
|
`[@reformer/core/behaviors] compute("${o}"): расходящийся цикл пересчёта — взаимные compute/computeFrom без стабилизации. Проверьте зависимости или добавьте стабилизирующее условие (when) / разорвите цикл через peek.`
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
|
-
throw
|
|
85
|
+
throw n;
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
-
function
|
|
90
|
-
x(e);
|
|
91
|
-
const o =
|
|
89
|
+
function L(e, t, n) {
|
|
90
|
+
k(e), s(() => x(e));
|
|
91
|
+
const o = j(e);
|
|
92
92
|
m(() => {
|
|
93
|
-
if (
|
|
94
|
-
const r =
|
|
93
|
+
if (n?.when && !n.when()) return;
|
|
94
|
+
const r = t();
|
|
95
95
|
e.peek() !== r && o(() => {
|
|
96
96
|
e.value = r;
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
-
function
|
|
101
|
-
x(
|
|
102
|
-
const r =
|
|
100
|
+
function Q(e, t, n, o) {
|
|
101
|
+
k(t), s(() => x(t));
|
|
102
|
+
const r = j(t);
|
|
103
103
|
m(() => {
|
|
104
104
|
const c = e.map((f) => f.value);
|
|
105
105
|
if (o?.when && !o.when(...c)) return;
|
|
106
|
-
const i =
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
const i = n(...c);
|
|
107
|
+
t.peek() !== i && r(() => {
|
|
108
|
+
t.value = i;
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
|
-
function
|
|
113
|
-
if (h(e) && h(
|
|
114
|
-
|
|
112
|
+
function U(e, t, n) {
|
|
113
|
+
if (h(e) && h(t)) {
|
|
114
|
+
s(S(e, t, n));
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
const o = e, r =
|
|
117
|
+
const o = e, r = t;
|
|
118
118
|
m(() => {
|
|
119
|
-
const c =
|
|
120
|
-
|
|
119
|
+
const c = E(o);
|
|
120
|
+
n?.when && !n.when() || _(() => W(r, c));
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
|
-
function
|
|
123
|
+
function R(e, t, n) {
|
|
124
124
|
let o;
|
|
125
125
|
const r = (c) => {
|
|
126
126
|
o?.abort(), o = new AbortController();
|
|
127
127
|
const { signal: i } = o;
|
|
128
|
-
$(() =>
|
|
128
|
+
$(() => t(c, { signal: i }));
|
|
129
129
|
};
|
|
130
|
-
if (!
|
|
131
|
-
|
|
130
|
+
if (!n?.debounce)
|
|
131
|
+
s(F(e, r, { immediate: n?.immediate }));
|
|
132
132
|
else {
|
|
133
133
|
let c;
|
|
134
134
|
const i = F(
|
|
135
135
|
e,
|
|
136
136
|
(f) => {
|
|
137
|
-
c && clearTimeout(c), c = setTimeout(() => r(f),
|
|
137
|
+
c && clearTimeout(c), c = setTimeout(() => r(f), n.debounce);
|
|
138
138
|
},
|
|
139
|
-
{ immediate:
|
|
139
|
+
{ immediate: n?.immediate }
|
|
140
140
|
);
|
|
141
|
-
|
|
141
|
+
s(() => {
|
|
142
142
|
c && clearTimeout(c), i();
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
-
|
|
145
|
+
s(() => o?.abort());
|
|
146
146
|
}
|
|
147
|
-
function
|
|
148
|
-
for (const o of
|
|
149
|
-
h(o) ?
|
|
147
|
+
function q(e, t, n) {
|
|
148
|
+
for (const o of B(e))
|
|
149
|
+
h(o) ? s(A(o, t, n)) : z(o, t, n);
|
|
150
150
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
151
|
+
function X(e, t, n) {
|
|
152
|
+
q(e, () => !t(), n);
|
|
153
153
|
}
|
|
154
|
-
function
|
|
155
|
-
const o =
|
|
154
|
+
function z(e, t, n) {
|
|
155
|
+
const o = I(e.__path);
|
|
156
156
|
o && m(() => {
|
|
157
|
-
const r =
|
|
158
|
-
|
|
159
|
-
r ? o.enable() : (o.disable(),
|
|
157
|
+
const r = t();
|
|
158
|
+
_(() => {
|
|
159
|
+
r ? o.enable() : (o.disable(), n?.resetOnDisable && o.reset());
|
|
160
160
|
});
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
|
-
function
|
|
164
|
-
|
|
163
|
+
function Y(e, t) {
|
|
164
|
+
s(C(e, t));
|
|
165
165
|
}
|
|
166
|
-
function
|
|
167
|
-
|
|
166
|
+
function Z(e, t, n) {
|
|
167
|
+
s(D(e, t, n));
|
|
168
168
|
}
|
|
169
|
-
function
|
|
170
|
-
|
|
169
|
+
function ee(e, t, n) {
|
|
170
|
+
s(M(e, t, n));
|
|
171
171
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
172
|
+
function ne(e, t) {
|
|
173
|
+
s(G(e, t));
|
|
174
174
|
}
|
|
175
|
-
function
|
|
176
|
-
return
|
|
175
|
+
function g(e, t) {
|
|
176
|
+
return t.split(".").reduce((n, o) => n?.[o], e);
|
|
177
177
|
}
|
|
178
|
-
function
|
|
178
|
+
function N(e) {
|
|
179
179
|
return new Proxy(
|
|
180
180
|
{},
|
|
181
181
|
{
|
|
182
|
-
get(
|
|
183
|
-
if (!(typeof
|
|
182
|
+
get(t, n) {
|
|
183
|
+
if (!(typeof n == "symbol" || n === "then"))
|
|
184
184
|
throw new Error(
|
|
185
185
|
`[@reformer/core/behaviors] applyEach: форма строки массива "${e}" недоступна (form.${String(
|
|
186
|
-
|
|
186
|
+
n
|
|
187
187
|
)}) — массив не материализован в схеме формы. Per-row node-операции (enableWhen/updateComponentProps/reset) требуют узла { array, item } в схеме; value-операции (row.$.*: compute/copyFrom/transformValue) работают и без материализации.`
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
);
|
|
192
192
|
}
|
|
193
|
-
function
|
|
194
|
-
const { model:
|
|
193
|
+
function H(e, t) {
|
|
194
|
+
const { model: n, form: o } = y(), r = e.__path;
|
|
195
195
|
if (!r) return;
|
|
196
|
-
const c =
|
|
196
|
+
const c = g(n, r);
|
|
197
197
|
if (!c) return;
|
|
198
198
|
const i = o.getFieldByPath(
|
|
199
199
|
r
|
|
200
|
-
), f = i?.length,
|
|
200
|
+
), f = i?.length, u = /* @__PURE__ */ new Map();
|
|
201
201
|
m(() => {
|
|
202
202
|
const d = f ? f.value : c.length, w = /* @__PURE__ */ new Set();
|
|
203
|
-
for (let
|
|
204
|
-
const l = c.at(
|
|
205
|
-
if (l != null && (w.add(l), !
|
|
206
|
-
const b = i?.at?.(
|
|
207
|
-
|
|
203
|
+
for (let a = 0; a < d; a++) {
|
|
204
|
+
const l = c.at(a);
|
|
205
|
+
if (l != null && (w.add(l), !u.has(l))) {
|
|
206
|
+
const b = i?.at?.(a) ?? N(r);
|
|
207
|
+
u.set(l, t.__run(l, b));
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
-
for (const [
|
|
211
|
-
w.has(
|
|
212
|
-
}),
|
|
213
|
-
for (const d of
|
|
214
|
-
|
|
210
|
+
for (const [a, l] of u)
|
|
211
|
+
w.has(a) || (l(), u.delete(a));
|
|
212
|
+
}), s(() => {
|
|
213
|
+
for (const d of u.values()) d();
|
|
214
|
+
u.clear();
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
217
|
function v(e) {
|
|
218
218
|
if (e == null || typeof e != "object") return;
|
|
219
|
-
const
|
|
220
|
-
if (typeof
|
|
221
|
-
const
|
|
222
|
-
for (let o = 0; o <
|
|
219
|
+
const t = e;
|
|
220
|
+
if (typeof t.at == "function" && typeof t.length == "number") {
|
|
221
|
+
const n = t.length;
|
|
222
|
+
for (let o = 0; o < n; o++) v(t.at(o));
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
|
-
for (const
|
|
225
|
+
for (const n of Object.keys(e)) v(e[n]);
|
|
226
226
|
}
|
|
227
|
-
function te(e,
|
|
228
|
-
const { model:
|
|
227
|
+
function te(e, t) {
|
|
228
|
+
const { model: n } = y(), o = e.__path;
|
|
229
229
|
if (!o) return;
|
|
230
|
-
const r =
|
|
231
|
-
r &&
|
|
230
|
+
const r = g(n, o);
|
|
231
|
+
r && H(
|
|
232
232
|
e,
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
T(({ model: c }) => {
|
|
234
|
+
R(t(c), (i) => {
|
|
235
235
|
if (i)
|
|
236
236
|
for (let f = 0; f < r.length; f++) {
|
|
237
|
-
const
|
|
238
|
-
if (
|
|
239
|
-
const d =
|
|
237
|
+
const u = r.at(f);
|
|
238
|
+
if (u && u !== c) {
|
|
239
|
+
const d = t(u);
|
|
240
240
|
d.peek() && (d.value = !1);
|
|
241
241
|
}
|
|
242
242
|
}
|
|
@@ -244,57 +244,57 @@ function te(e, n) {
|
|
|
244
244
|
})
|
|
245
245
|
);
|
|
246
246
|
}
|
|
247
|
-
function
|
|
248
|
-
const { model:
|
|
247
|
+
function oe(e, t) {
|
|
248
|
+
const { model: n } = y(), o = e.__path;
|
|
249
249
|
if (!o) return;
|
|
250
|
-
const r =
|
|
250
|
+
const r = g(n, o);
|
|
251
251
|
if (!r) return;
|
|
252
252
|
let c = !1, i = 0;
|
|
253
253
|
m(() => {
|
|
254
|
-
v(r), !c && (c = !0,
|
|
254
|
+
v(r), !c && (c = !0, _(() => {
|
|
255
255
|
c = !1;
|
|
256
|
-
const f =
|
|
257
|
-
let
|
|
256
|
+
const f = t(r.toArray());
|
|
257
|
+
let u = !1;
|
|
258
258
|
for (const { index: d, patch: w } of f) {
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
259
|
+
const a = r.at(d);
|
|
260
|
+
if (a)
|
|
261
261
|
for (const [l, b] of Object.entries(w))
|
|
262
|
-
|
|
262
|
+
a[l] !== b && (a[l] = b, u = !0);
|
|
263
263
|
}
|
|
264
|
-
if (i =
|
|
264
|
+
if (i = u ? i + 1 : 0, i > 50)
|
|
265
265
|
throw i = 0, new Error(
|
|
266
266
|
`[@reformer/core/behaviors] aggregateInto("${o}"): запись не сходится (>50 проходов) — derive должна быть идемпотентной на фикспоинте.`
|
|
267
267
|
);
|
|
268
268
|
}));
|
|
269
269
|
});
|
|
270
270
|
}
|
|
271
|
-
function
|
|
272
|
-
const { form:
|
|
273
|
-
for (const o of
|
|
271
|
+
function re(e, t) {
|
|
272
|
+
const { form: n } = y();
|
|
273
|
+
for (const o of B(e)) {
|
|
274
274
|
const r = o, c = r.__path;
|
|
275
275
|
if (!c) continue;
|
|
276
|
-
const i =
|
|
277
|
-
|
|
276
|
+
const i = n ? n.getFieldByPath(c) : void 0, f = i ? i.getProxy?.() ?? i : void 0;
|
|
277
|
+
s(t.__run(V(r), f));
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
export {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
281
|
+
oe as aggregateInto,
|
|
282
|
+
re as apply,
|
|
283
|
+
H as applyEach,
|
|
284
|
+
L as compute,
|
|
285
|
+
Q as computeFrom,
|
|
286
|
+
U as copyFrom,
|
|
287
|
+
_ as defer,
|
|
288
|
+
T as defineFormBehavior,
|
|
289
|
+
X as disableWhen,
|
|
290
290
|
m as effect,
|
|
291
|
-
|
|
291
|
+
q as enableWhen,
|
|
292
292
|
te as exclusiveFlag,
|
|
293
293
|
y as getScope,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
R as onChange,
|
|
295
|
+
s as onDispose,
|
|
296
|
+
Z as resetWhen,
|
|
297
|
+
ne as revalidateWhen,
|
|
298
|
+
ee as syncFields,
|
|
299
|
+
Y as transformValue
|
|
300
300
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FormNode } from '../nodes/form-node';
|
|
2
|
+
import { ConfigWithSchema, ConfigWithValue, UnknownRecord } from '../types';
|
|
2
3
|
/**
|
|
3
4
|
* Фабрика для создания узлов формы.
|
|
4
5
|
*
|
|
@@ -59,7 +60,7 @@ export declare class NodeFactory {
|
|
|
59
60
|
* ]);
|
|
60
61
|
* ```
|
|
61
62
|
*/
|
|
62
|
-
createNode
|
|
63
|
+
createNode(config: unknown): FormNode<unknown>;
|
|
63
64
|
/**
|
|
64
65
|
* Создать ArrayNode из массива [schema, ...initialItems]
|
|
65
66
|
*
|
|
@@ -135,7 +136,7 @@ export declare class NodeFactory {
|
|
|
135
136
|
* factory.isFieldConfig(null); // false
|
|
136
137
|
* ```
|
|
137
138
|
*/
|
|
138
|
-
isFieldConfig(config: unknown):
|
|
139
|
+
isFieldConfig(config: unknown): config is ConfigWithValue;
|
|
139
140
|
/**
|
|
140
141
|
* Проверяет, является ли конфиг конфигурацией массива (ArrayConfig)
|
|
141
142
|
*
|
|
@@ -157,7 +158,7 @@ export declare class NodeFactory {
|
|
|
157
158
|
* factory.isArrayConfig({ email: { value: '' } }); // false
|
|
158
159
|
* ```
|
|
159
160
|
*/
|
|
160
|
-
isArrayConfig(config: unknown):
|
|
161
|
+
isArrayConfig(config: unknown): config is ConfigWithSchema;
|
|
161
162
|
/**
|
|
162
163
|
* Проверяет, является ли конфиг конфигурацией группы (GroupConfig)
|
|
163
164
|
*
|
|
@@ -183,5 +184,5 @@ export declare class NodeFactory {
|
|
|
183
184
|
* factory.isGroupConfig(null); // false
|
|
184
185
|
* ```
|
|
185
186
|
*/
|
|
186
|
-
isGroupConfig(config: unknown):
|
|
187
|
+
isGroupConfig(config: unknown): config is UnknownRecord;
|
|
187
188
|
}
|
|
@@ -57,10 +57,25 @@ export declare function watchField<T>(source: ReadonlySignal<T>, cb: (value: T)
|
|
|
57
57
|
* ⚠️ Поле должно быть материализовано в форме (`createForm`) — иначе ноды в реестре нет (например,
|
|
58
58
|
* элемент массива, который строится per-item).
|
|
59
59
|
*
|
|
60
|
+
* ⚠️ Если то же поле одновременно является целью `compute`/`computeFrom`, обязательно ограничь
|
|
61
|
+
* compute тем же условием (`when`), что и это `enableWhen`. Механизмы не согласованы: `enableWhen`
|
|
62
|
+
* работает со статус-машиной ноды (enable/disable/reset), а `compute` пишет `target.value` напрямую
|
|
63
|
+
* в сигнал модели и проверяет только собственный `options.when` — состояние `disabled` он НЕ смотрит.
|
|
64
|
+
* Поэтому при выключенном поле любое изменение зависимости compute молча заново заполнит его (а с
|
|
65
|
+
* `resetOnDisable` — перезапишет только что сброшенное значение), и так как `getValue()`/`submit`
|
|
66
|
+
* включают disabled-поля, мусор утечёт в payload. Это аналог правила #13 гайда add-behavior,
|
|
67
|
+
* которое сегодня сформулировано только для `copyFrom`; для `compute` то же ограничение обязательно.
|
|
68
|
+
*
|
|
60
69
|
* @group Model
|
|
61
70
|
* @example
|
|
62
71
|
* ```typescript
|
|
63
72
|
* enableWhen(model.$.propertyValue, () => model.loanType === 'mortgage', { resetOnDisable: true });
|
|
73
|
+
*
|
|
74
|
+
* // Поле-цель compute, которое ТАКЖЕ в resetOnDisable enableWhen: compute обязан нести тот же when,
|
|
75
|
+
* // иначе initialPayment заново заполнится в выключенном состоянии и утечёт в submit.
|
|
76
|
+
* compute(model.$.initialPayment, () => model.propertyValue * 0.2, {
|
|
77
|
+
* when: () => model.loanType === 'mortgage',
|
|
78
|
+
* });
|
|
64
79
|
* ```
|
|
65
80
|
*/
|
|
66
81
|
export declare function enableWhen(target: ReadonlySignal<unknown>, condition: () => boolean, options?: {
|
|
@@ -14,6 +14,14 @@ export type PathAwareSignal<T> = Signal<T> & {
|
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
16
|
type Opaque = Date | File | Blob;
|
|
17
|
+
/**
|
|
18
|
+
* Фасад элемента массива при обходе (`at`/`map`/`forEach`): массив → {@link ModelArray},
|
|
19
|
+
* Opaque (Date/File/Blob) и примитив → значение, объект → под-модель {@link FormModel}.
|
|
20
|
+
* Зеркалит листовой/объектный сплит {@link ModelValue} (но объект → под-модель с API),
|
|
21
|
+
* чтобы `files: File[]`/`dates: Date[]`/вложенные массивы не мис-типизировались как `FormModel`.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
type ModelArrayItem<U> = NonNullable<U> extends ReadonlyArray<infer E> ? ModelArray<E> : NonNullable<U> extends Opaque ? U : NonNullable<U> extends object ? FormModel<NonNullable<U>> : U;
|
|
17
25
|
/**
|
|
18
26
|
* Значение поля в value-доступе модели:
|
|
19
27
|
* - массив → {@link ModelArray}
|
|
@@ -40,6 +48,11 @@ export type ModelObject<T> = {
|
|
|
40
48
|
* @group Model
|
|
41
49
|
*/
|
|
42
50
|
export interface ModelArray<U> {
|
|
51
|
+
/**
|
|
52
|
+
* Путь массива в модели (dot-нотация). Предоставляется рантаймом (value-прокси) и требуется
|
|
53
|
+
* рендер-слою для резолва узла массива (напр. `ArrayRenderNode` в `@reformer/renderer-react`).
|
|
54
|
+
*/
|
|
55
|
+
readonly __path: string;
|
|
43
56
|
/** Реактивная длина. */
|
|
44
57
|
readonly length: number;
|
|
45
58
|
/** Добавить элемент в конец (значение элемента целиком). */
|
|
@@ -54,12 +67,12 @@ export interface ModelArray<U> {
|
|
|
54
67
|
swap(a: number, b: number): void;
|
|
55
68
|
/** Очистить массив. */
|
|
56
69
|
clear(): void;
|
|
57
|
-
/**
|
|
58
|
-
at(index: number): U extends object ?
|
|
59
|
-
/** Map по
|
|
60
|
-
map<R>(fn: (item:
|
|
61
|
-
/** Итерация по
|
|
62
|
-
forEach(fn: (item:
|
|
70
|
+
/** Элемент по индексу: объект → под-модель, массив → {@link ModelArray}, лист → значение. */
|
|
71
|
+
at(index: number): NonNullable<U> extends object ? ModelArrayItem<U> : ModelArrayItem<U> | undefined;
|
|
72
|
+
/** Map по элементам (объект → {@link FormModel}, Opaque/примитив → значение, массив → {@link ModelArray}). */
|
|
73
|
+
map<R>(fn: (item: ModelArrayItem<U>, index: number) => R): R[];
|
|
74
|
+
/** Итерация по элементам (см. {@link ModelArrayItem}). */
|
|
75
|
+
forEach(fn: (item: ModelArrayItem<U>, index: number) => void): void;
|
|
63
76
|
/** Снимок массива значений (без подписки). */
|
|
64
77
|
toArray(): U[];
|
|
65
78
|
/** Индексный value-доступ. */
|
|
@@ -72,11 +85,19 @@ export interface ModelArray<U> {
|
|
|
72
85
|
* @group Model
|
|
73
86
|
*/
|
|
74
87
|
export type ModelSignals<T> = {
|
|
75
|
-
[K in keyof T]:
|
|
76
|
-
readonly length: number;
|
|
77
|
-
readonly [index: number]: ModelSignals<U & object>;
|
|
78
|
-
} : NonNullable<T[K]> extends Opaque ? PathAwareSignal<T[K]> : NonNullable<T[K]> extends object ? ModelSignals<NonNullable<T[K]>> : PathAwareSignal<T[K]>;
|
|
88
|
+
[K in keyof T]: ModelSignalNode<T[K]>;
|
|
79
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Узел дерева сигналов `model.$`: массив → индексируемый узел под-сигналов, Opaque (Date/File/Blob)
|
|
92
|
+
* и примитив → {@link PathAwareSignal}, объект → вложенное дерево {@link ModelSignals}. Для примитивных
|
|
93
|
+
* и Opaque массивов элемент — сам сигнал листа (а не под-дерево), поэтому `model.$.tags[0]` — это
|
|
94
|
+
* `PathAwareSignal<string>`, а не `ModelSignals<never>`.
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
type ModelSignalNode<V> = NonNullable<V> extends ReadonlyArray<infer U> ? {
|
|
98
|
+
readonly length: number;
|
|
99
|
+
readonly [index: number]: ModelSignalNode<U>;
|
|
100
|
+
} : NonNullable<V> extends Opaque ? PathAwareSignal<V> : NonNullable<V> extends object ? ModelSignals<NonNullable<V>> : PathAwareSignal<V>;
|
|
80
101
|
/**
|
|
81
102
|
* API уровня модели (доступно на корне и под-моделях элементов массива).
|
|
82
103
|
*
|
|
@@ -90,9 +111,17 @@ export interface ModelApi<T> {
|
|
|
90
111
|
readonly $: ModelSignals<T>;
|
|
91
112
|
/** Снимок значений (без подписки) — для submit. */
|
|
92
113
|
get(): T;
|
|
93
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* @deprecated Алиас {@link ModelApi.patch}: `set` выполняет то же частичное **слияние** —
|
|
116
|
+
* обновляет только переданные ключи, отсутствующие НЕ трогает (это НЕ полная замена, вопреки
|
|
117
|
+
* старому названию). Используйте `patch`; для замены с обнулением лишних полей делайте
|
|
118
|
+
* `patch({ ...defaults, ...dto })` явно. Не меняет initial-снимок.
|
|
119
|
+
*/
|
|
94
120
|
set(value: Partial<T>): void;
|
|
95
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Частичное слияние значений (load/patch с сервера): обновляет только переданные ключи,
|
|
123
|
+
* отсутствующие ключи НЕ трогаются. Не меняет initial-снимок.
|
|
124
|
+
*/
|
|
96
125
|
patch(value: Partial<T>): void;
|
|
97
126
|
/** Отличаются ли текущие значения от initial-снимка (value-diff). */
|
|
98
127
|
isDirty(): boolean;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { ValidationError } from '../types';
|
|
2
|
+
import { FormSchemaNode } from '../types/schema-node';
|
|
2
3
|
import { FormModel } from './types';
|
|
3
4
|
/**
|
|
4
|
-
* Валидатор слоя
|
|
5
|
+
* Валидатор слоя **данных**. `value` — значение поля; `model` — ближайший scope (под-модель элемента
|
|
5
6
|
* массива или корень); `root` — корневая модель.
|
|
6
7
|
*
|
|
8
|
+
* Отличие от {@link Validator}: 2-й/3-й аргументы — сами данные ({@link FormModel}/scope), а не
|
|
9
|
+
* `FormProxy`-узлы формы. Оба совместимы с полем `validators` узла схемы (см. {@link SchemaValidator}).
|
|
10
|
+
*
|
|
7
11
|
* @group Model
|
|
8
12
|
*/
|
|
9
13
|
export type ModelValidator<TValue = unknown, TModel = unknown, TRoot = unknown> = (value: TValue, model: TModel, root: TRoot) => ValidationError | null | Promise<ValidationError | null>;
|
|
@@ -32,7 +36,7 @@ export interface ModelValidationResult {
|
|
|
32
36
|
* if (!res.valid) console.log(res.errors); // { 'email': [{ code, message }], ... }
|
|
33
37
|
* ```
|
|
34
38
|
*/
|
|
35
|
-
export declare function validateModelSync<T>(model: FormModel<T>, schema:
|
|
39
|
+
export declare function validateModelSync<T>(model: FormModel<T>, schema: FormSchemaNode): ModelValidationResult;
|
|
36
40
|
/**
|
|
37
41
|
* Headless-валидация данных (sync + async). Работает без UI/нод.
|
|
38
42
|
*
|
|
@@ -43,7 +47,7 @@ export declare function validateModelSync<T>(model: FormModel<T>, schema: unknow
|
|
|
43
47
|
* if (!res.valid) console.log(res.errors); // { 'email': [{ code, message }], ... }
|
|
44
48
|
* ```
|
|
45
49
|
*/
|
|
46
|
-
export declare function validateModel<T>(model: FormModel<T>, schema:
|
|
50
|
+
export declare function validateModel<T>(model: FormModel<T>, schema: FormSchemaNode): Promise<ModelValidationResult>;
|
|
47
51
|
/**
|
|
48
52
|
* In-form валидация: прогоняет {@link validateModel} и роутит ошибки в ноды формы
|
|
49
53
|
* (через реестр сигнал→нода): `node.setErrors(errors[path] ?? [])` — заодно очищает прошедшие поля
|
|
@@ -65,4 +69,4 @@ export declare function validateModel<T>(model: FormModel<T>, schema: unknown):
|
|
|
65
69
|
* if (res.valid) await api.save(model.get());
|
|
66
70
|
* ```
|
|
67
71
|
*/
|
|
68
|
-
export declare function validateFormModel<T>(model: FormModel<T>, schema:
|
|
72
|
+
export declare function validateFormModel<T>(model: FormModel<T>, schema: FormSchemaNode): Promise<ModelValidationResult>;
|
|
@@ -35,7 +35,7 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
35
35
|
readonly status: ReadonlySignal<FieldStatus>;
|
|
36
36
|
readonly disabled: ReadonlySignal<boolean>;
|
|
37
37
|
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
38
|
-
readonly componentProps: ReadonlySignal<Record<string,
|
|
38
|
+
readonly componentProps: ReadonlySignal<Record<string, unknown>>;
|
|
39
39
|
/**
|
|
40
40
|
* Вычисляемое свойство: нужно ли показывать ошибку
|
|
41
41
|
* Ошибка показывается если поле невалидно И (touched ИЛИ dirty)
|
|
@@ -186,7 +186,7 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
186
186
|
* });
|
|
187
187
|
* ```
|
|
188
188
|
*/
|
|
189
|
-
updateComponentProps(props: Partial<Record<string,
|
|
189
|
+
updateComponentProps(props: Partial<Record<string, unknown>>): void;
|
|
190
190
|
/**
|
|
191
191
|
* Динамически изменяет триггер валидации (updateOn)
|
|
192
192
|
* Полезно для адаптивной валидации - например, переключиться на instant feedback после первого submit
|