adaptform 2.0.5 → 2.2.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/CHANGELOG.md +151 -2
- package/CHANGELOG.ru.md +200 -0
- package/LICENSE.md +3 -1
- package/LICENSE.ru.md +25 -0
- package/README.md +299 -297
- package/README.ru.md +422 -0
- package/dist/Form-uHUaTUZS.d.cts +387 -0
- package/dist/Form-uHUaTUZS.d.ts +387 -0
- package/dist/index.cjs +864 -283
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.mjs +863 -284
- package/dist/index.mjs.map +1 -1
- package/dist/luxon.d.cts +1 -1
- package/dist/luxon.d.ts +1 -1
- package/package.json +28 -9
- package/dist/Form-Be3I6LX0.d.cts +0 -293
- package/dist/Form-Be3I6LX0.d.ts +0 -293
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,334 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/core/state/value.ts
|
|
4
|
+
function copyValue(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
5
|
+
if (value === null || typeof value !== "object") return value;
|
|
6
|
+
if (seen.has(value)) return seen.get(value);
|
|
7
|
+
const remember = (copy) => {
|
|
8
|
+
seen.set(value, copy);
|
|
9
|
+
return copy;
|
|
10
|
+
};
|
|
11
|
+
if (value instanceof Date) return remember(new Date(value.getTime()));
|
|
12
|
+
if (value instanceof RegExp) return remember(new RegExp(value.source, value.flags));
|
|
13
|
+
if (value instanceof ArrayBuffer) return remember(value.slice(0));
|
|
14
|
+
if (ArrayBuffer.isView(value)) {
|
|
15
|
+
const bytes = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
16
|
+
return remember(value instanceof DataView ? new DataView(bytes) : new value.constructor(bytes));
|
|
17
|
+
}
|
|
18
|
+
if (value instanceof Map) {
|
|
19
|
+
const result = /* @__PURE__ */ new Map();
|
|
20
|
+
seen.set(value, result);
|
|
21
|
+
for (const [key, item] of value) result.set(copyValue(key, seen), copyValue(item, seen));
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof Set) {
|
|
25
|
+
const result = /* @__PURE__ */ new Set();
|
|
26
|
+
seen.set(value, result);
|
|
27
|
+
for (const item of value) result.add(copyValue(item, seen));
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value) || Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) {
|
|
31
|
+
const result = Array.isArray(value) ? new Array(value.length) : Object.create(Object.getPrototypeOf(value));
|
|
32
|
+
seen.set(value, result);
|
|
33
|
+
for (const key of Object.keys(value)) Object.defineProperty(result, key, {
|
|
34
|
+
value: copyValue(value[key], seen),
|
|
35
|
+
enumerable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
configurable: true
|
|
38
|
+
});
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
function equalValue(a, b, seen = /* @__PURE__ */ new WeakMap()) {
|
|
44
|
+
if (Object.is(a, b)) return true;
|
|
45
|
+
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
46
|
+
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;
|
|
47
|
+
if (seen.get(a)?.has(b)) return true;
|
|
48
|
+
const matches = seen.get(a) ?? /* @__PURE__ */ new WeakSet();
|
|
49
|
+
matches.add(b);
|
|
50
|
+
seen.set(a, matches);
|
|
51
|
+
if (a instanceof Date && b instanceof Date) return Object.is(a.getTime(), b.getTime());
|
|
52
|
+
if (a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
|
|
53
|
+
if (a instanceof ArrayBuffer && b instanceof ArrayBuffer) return equalValue(new Uint8Array(a), new Uint8Array(b), seen);
|
|
54
|
+
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
|
|
55
|
+
const av = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
|
|
56
|
+
const bv = new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
|
|
57
|
+
return av.length === bv.length && av.every((byte, i) => byte === bv[i]);
|
|
58
|
+
}
|
|
59
|
+
if (a instanceof Map && b instanceof Map) return equalValue([...a], [...b], seen);
|
|
60
|
+
if (a instanceof Set && b instanceof Set) return equalValue([...a], [...b], seen);
|
|
61
|
+
if ("isLuxonDateTime" in a && a.isLuxonDateTime === true && "equals" in a && typeof a.equals === "function") {
|
|
62
|
+
return a.equals(b);
|
|
63
|
+
}
|
|
64
|
+
if (!Array.isArray(a) && Object.getPrototypeOf(a) !== Object.prototype && Object.getPrototypeOf(a) !== null) return false;
|
|
65
|
+
if (Array.isArray(a) && Array.isArray(b) && a.length !== b.length) return false;
|
|
66
|
+
const keys = Object.keys(a);
|
|
67
|
+
return keys.length === Object.keys(b).length && keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && equalValue(a[key], b[key], seen));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/core/state/snapshot.ts
|
|
71
|
+
function snapshotValue(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
72
|
+
if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return value;
|
|
73
|
+
if (typeof value !== "object") return Object.freeze({ kind: "Opaque", type: typeof value });
|
|
74
|
+
const previous = seen.get(value);
|
|
75
|
+
if (previous) return previous;
|
|
76
|
+
if (value instanceof Date) return Object.freeze({ kind: "Date", timestamp: value.getTime() });
|
|
77
|
+
if (value instanceof RegExp) return Object.freeze({ kind: "RegExp", source: value.source, flags: value.flags });
|
|
78
|
+
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
79
|
+
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
80
|
+
return Object.freeze({ kind: value.constructor.name, bytes: Object.freeze([...bytes]) });
|
|
81
|
+
}
|
|
82
|
+
if ("isLuxonDateTime" in value && value.isLuxonDateTime === true && "toISO" in value && typeof value.toISO === "function") {
|
|
83
|
+
return Object.freeze({ kind: "DateTime", iso: value.toISO() });
|
|
84
|
+
}
|
|
85
|
+
if (value instanceof Map || value instanceof Set) {
|
|
86
|
+
const result = { kind: value instanceof Map ? "Map" : "Set" };
|
|
87
|
+
seen.set(value, result);
|
|
88
|
+
result.entries = snapshotValue([...value], seen);
|
|
89
|
+
return Object.freeze(result);
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
const result = [];
|
|
93
|
+
seen.set(value, result);
|
|
94
|
+
for (const item of value) result.push(snapshotValue(item, seen));
|
|
95
|
+
return Object.freeze(result);
|
|
96
|
+
}
|
|
97
|
+
if (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) {
|
|
98
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
99
|
+
seen.set(value, result);
|
|
100
|
+
for (const [key, item] of Object.entries(value)) result[key] = snapshotValue(item, seen);
|
|
101
|
+
return Object.freeze(result);
|
|
102
|
+
}
|
|
103
|
+
return Object.freeze({ kind: "Opaque", type: value.constructor?.name ?? "object" });
|
|
104
|
+
}
|
|
105
|
+
function freezeSnapshot(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
106
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return value;
|
|
107
|
+
seen.add(value);
|
|
108
|
+
for (const item of Object.values(value)) freezeSnapshot(item, seen);
|
|
109
|
+
return Object.freeze(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/core/state/ObservableNode.ts
|
|
113
|
+
var mutationDepth = 0;
|
|
114
|
+
var flushing = false;
|
|
115
|
+
var publishing = false;
|
|
116
|
+
var batchDepth = 0;
|
|
117
|
+
var validations = /* @__PURE__ */ new Map();
|
|
118
|
+
var batchRoots = /* @__PURE__ */ new Map();
|
|
119
|
+
var pending = /* @__PURE__ */ new Set();
|
|
120
|
+
function flush() {
|
|
121
|
+
if (mutationDepth || flushing) return;
|
|
122
|
+
flushing = true;
|
|
123
|
+
let failure;
|
|
124
|
+
let failed = false;
|
|
125
|
+
try {
|
|
126
|
+
while (pending.size) {
|
|
127
|
+
const stores = [...pending];
|
|
128
|
+
const before = stores.map((store) => store.committed());
|
|
129
|
+
pending.clear();
|
|
130
|
+
for (const store of stores) store.refresh();
|
|
131
|
+
const changed = stores.filter((store, index) => !equalValue(before[index], store.committed()));
|
|
132
|
+
publishing = true;
|
|
133
|
+
try {
|
|
134
|
+
for (const store of changed) {
|
|
135
|
+
try {
|
|
136
|
+
store.notify();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (!failed) {
|
|
139
|
+
failure = error;
|
|
140
|
+
failed = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} finally {
|
|
145
|
+
publishing = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} finally {
|
|
149
|
+
flushing = false;
|
|
150
|
+
}
|
|
151
|
+
if (failed) throw failure;
|
|
152
|
+
}
|
|
153
|
+
var SnapshotStore = class {
|
|
154
|
+
constructor(capture, getChildren) {
|
|
155
|
+
this.capture = capture;
|
|
156
|
+
this.getChildren = getChildren;
|
|
157
|
+
this.initialized = false;
|
|
158
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
159
|
+
this.parents = /* @__PURE__ */ new Set();
|
|
160
|
+
this.children = /* @__PURE__ */ new Set();
|
|
161
|
+
}
|
|
162
|
+
reconcile(seen = /* @__PURE__ */ new Set()) {
|
|
163
|
+
if (seen.has(this)) return;
|
|
164
|
+
seen.add(this);
|
|
165
|
+
const next = new Set(this.listeners.size || this.parents.size ? this.getChildren() : []);
|
|
166
|
+
for (const child of this.children) {
|
|
167
|
+
if (!next.has(child)) {
|
|
168
|
+
child.parents.delete(this);
|
|
169
|
+
child.reconcile(seen);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const previous = this.children;
|
|
173
|
+
this.children = next;
|
|
174
|
+
for (const child of next) {
|
|
175
|
+
if (!previous.has(child)) {
|
|
176
|
+
child.parents.add(this);
|
|
177
|
+
child.reconcile(seen);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
read() {
|
|
182
|
+
if (!mutationDepth && !publishing) this.refresh();
|
|
183
|
+
else if (!this.initialized) this.refresh();
|
|
184
|
+
return this.snapshot;
|
|
185
|
+
}
|
|
186
|
+
committed() {
|
|
187
|
+
return this.snapshot;
|
|
188
|
+
}
|
|
189
|
+
refresh() {
|
|
190
|
+
this.reconcile();
|
|
191
|
+
const next = freezeSnapshot(this.capture());
|
|
192
|
+
if (this.initialized && equalValue(this.snapshot, next)) return false;
|
|
193
|
+
this.snapshot = next;
|
|
194
|
+
this.initialized = true;
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
subscribe(listener) {
|
|
198
|
+
this.read();
|
|
199
|
+
const registration = () => listener();
|
|
200
|
+
this.listeners.add(registration);
|
|
201
|
+
this.reconcile();
|
|
202
|
+
return () => {
|
|
203
|
+
this.listeners.delete(registration);
|
|
204
|
+
this.reconcile();
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
changed(seen = /* @__PURE__ */ new Set()) {
|
|
208
|
+
if (seen.has(this)) return;
|
|
209
|
+
seen.add(this);
|
|
210
|
+
this.reconcile();
|
|
211
|
+
if (this.listeners.size || this.parents.size || this.initialized) pending.add(this);
|
|
212
|
+
for (const parent of this.parents) parent.changed(seen);
|
|
213
|
+
}
|
|
214
|
+
notify() {
|
|
215
|
+
let failure;
|
|
216
|
+
let failed = false;
|
|
217
|
+
for (const listener of [...this.listeners]) {
|
|
218
|
+
if (!this.listeners.has(listener)) continue;
|
|
219
|
+
try {
|
|
220
|
+
listener();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (!failed) {
|
|
223
|
+
failure = error;
|
|
224
|
+
failed = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (failed) throw failure;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var ObservableNode = class {
|
|
232
|
+
constructor() {
|
|
233
|
+
this._stateStore = new SnapshotStore(
|
|
234
|
+
() => this.captureState(),
|
|
235
|
+
() => this.stateChildren().map((child) => child._stateStore)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
stateChildren() {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
contains(node, seen = /* @__PURE__ */ new Set()) {
|
|
242
|
+
if (this === node) return true;
|
|
243
|
+
if (seen.has(this)) return false;
|
|
244
|
+
seen.add(this);
|
|
245
|
+
return this.stateChildren().some((child) => child.contains(node, seen));
|
|
246
|
+
}
|
|
247
|
+
validateAfterMutation(callback) {
|
|
248
|
+
if (batchDepth) validations.set(this, callback);
|
|
249
|
+
else callback();
|
|
250
|
+
}
|
|
251
|
+
batchUpdate(callback, validate) {
|
|
252
|
+
this.getSnapshot();
|
|
253
|
+
return this.update(() => {
|
|
254
|
+
batchDepth++;
|
|
255
|
+
batchRoots.set(this, validate);
|
|
256
|
+
let failed = false;
|
|
257
|
+
let failure;
|
|
258
|
+
let result;
|
|
259
|
+
try {
|
|
260
|
+
result = callback();
|
|
261
|
+
if (result !== null && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") {
|
|
262
|
+
throw new TypeError("batch callbacks must be synchronous");
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
failed = true;
|
|
266
|
+
failure = error;
|
|
267
|
+
} finally {
|
|
268
|
+
batchDepth--;
|
|
269
|
+
}
|
|
270
|
+
if (!batchDepth) {
|
|
271
|
+
const requested = [...validations];
|
|
272
|
+
const roots = [...batchRoots];
|
|
273
|
+
validations.clear();
|
|
274
|
+
batchRoots.clear();
|
|
275
|
+
const selected = roots.filter(([root]) => requested.some(([node]) => root.contains(node))).filter(([root], index, all) => !all.some(([other], otherIndex) => other !== root && other.contains(root) && (!root.contains(other) || otherIndex < index)));
|
|
276
|
+
const checks = [
|
|
277
|
+
...selected.map(([, check]) => check),
|
|
278
|
+
...requested.filter(([node]) => !selected.some(([root]) => root.contains(node))).map(([, check]) => check)
|
|
279
|
+
];
|
|
280
|
+
for (const check of checks) {
|
|
281
|
+
try {
|
|
282
|
+
check();
|
|
283
|
+
} catch (error) {
|
|
284
|
+
if (!failed) {
|
|
285
|
+
failed = true;
|
|
286
|
+
failure = error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (failed) throw failure;
|
|
292
|
+
return result;
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
subscribe(listener) {
|
|
296
|
+
return this._stateStore.subscribe(listener);
|
|
297
|
+
}
|
|
298
|
+
getSnapshot() {
|
|
299
|
+
return this._stateStore.read();
|
|
300
|
+
}
|
|
301
|
+
update(callback) {
|
|
302
|
+
mutationDepth++;
|
|
303
|
+
let failed = false;
|
|
304
|
+
try {
|
|
305
|
+
return callback();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
failed = true;
|
|
308
|
+
throw error;
|
|
309
|
+
} finally {
|
|
310
|
+
this._stateStore.changed();
|
|
311
|
+
mutationDepth--;
|
|
312
|
+
if (failed) {
|
|
313
|
+
try {
|
|
314
|
+
flush();
|
|
315
|
+
} catch {
|
|
316
|
+
}
|
|
317
|
+
} else flush();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
3
322
|
// src/core/fields/Field.ts
|
|
4
|
-
var Field = class {
|
|
323
|
+
var Field = class extends ObservableNode {
|
|
5
324
|
constructor(TypeConstructor, defaultValue, options) {
|
|
325
|
+
super();
|
|
6
326
|
this._rawValue = null;
|
|
7
327
|
this._valueClear = null;
|
|
328
|
+
this._baseline = null;
|
|
8
329
|
this._errors = [];
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
330
|
+
this._isValid = null;
|
|
331
|
+
this._isTouched = false;
|
|
11
332
|
this.fieldType = new TypeConstructor(options ?? {});
|
|
12
333
|
this.defaultValue = defaultValue ?? null;
|
|
13
334
|
for (const plugin of this.options.plugins ?? []) {
|
|
@@ -20,8 +341,36 @@ var Field = class {
|
|
|
20
341
|
this._valueClear = null;
|
|
21
342
|
this.isValid = null;
|
|
22
343
|
}
|
|
344
|
+
this.isTouched = false;
|
|
345
|
+
this.commitBaseline();
|
|
346
|
+
}
|
|
347
|
+
get isValid() {
|
|
348
|
+
return this._isValid;
|
|
349
|
+
}
|
|
350
|
+
set isValid(value) {
|
|
351
|
+
this.update(() => {
|
|
352
|
+
this._isValid = value;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
get isTouched() {
|
|
356
|
+
return this._isTouched;
|
|
357
|
+
}
|
|
358
|
+
set isTouched(value) {
|
|
359
|
+
this.update(() => {
|
|
360
|
+
this._isTouched = value;
|
|
361
|
+
});
|
|
23
362
|
}
|
|
24
|
-
|
|
363
|
+
captureState() {
|
|
364
|
+
return {
|
|
365
|
+
rawValue: snapshotValue(this._rawValue),
|
|
366
|
+
valueClear: snapshotValue(this._valueClear),
|
|
367
|
+
errors: [...this._errors],
|
|
368
|
+
isValid: this.isValid,
|
|
369
|
+
isTouched: this.isTouched,
|
|
370
|
+
isDirty: this.isDirty
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
// ========== Getters and setters ==========
|
|
25
374
|
get plugins() {
|
|
26
375
|
return [...this.options.plugins ?? []].sort((a, b) => {
|
|
27
376
|
return (a.pluginOptions?.priority ?? 0) - (b.pluginOptions?.priority ?? 0);
|
|
@@ -31,13 +380,30 @@ var Field = class {
|
|
|
31
380
|
return this.fieldType.options;
|
|
32
381
|
}
|
|
33
382
|
set options(options) {
|
|
34
|
-
|
|
35
|
-
|
|
383
|
+
this.update(() => {
|
|
384
|
+
const currentOptions = this.fieldType.options;
|
|
385
|
+
this.fieldType.options = Object.assign(currentOptions, options);
|
|
386
|
+
});
|
|
36
387
|
}
|
|
37
388
|
get rawValue() {
|
|
38
389
|
return this._rawValue;
|
|
39
390
|
}
|
|
40
391
|
set rawValue(input) {
|
|
392
|
+
this.setRawValue(input);
|
|
393
|
+
}
|
|
394
|
+
setRawValue(input, validate = true) {
|
|
395
|
+
return this.update(() => {
|
|
396
|
+
this.assignRawValue(input);
|
|
397
|
+
this._errors = [];
|
|
398
|
+
this.isTouched = true;
|
|
399
|
+
if (validate) {
|
|
400
|
+
this.validateAfterMutation(() => this.checkAndSetValid());
|
|
401
|
+
} else {
|
|
402
|
+
this.isValid = null;
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
assignRawValue(input) {
|
|
41
407
|
let rawValue = input;
|
|
42
408
|
for (const plugin of this.plugins) {
|
|
43
409
|
if (plugin.toRawValue) {
|
|
@@ -52,9 +418,6 @@ var Field = class {
|
|
|
52
418
|
}
|
|
53
419
|
this._rawValue = rawValue;
|
|
54
420
|
this._valueClear = this.fieldType.cast(valueClear);
|
|
55
|
-
this._errors = [];
|
|
56
|
-
this.isTouched = true;
|
|
57
|
-
this.checkAndSetValid();
|
|
58
421
|
}
|
|
59
422
|
get errors() {
|
|
60
423
|
return this._errors;
|
|
@@ -63,17 +426,27 @@ var Field = class {
|
|
|
63
426
|
return this._errors[0] ?? "";
|
|
64
427
|
}
|
|
65
428
|
set error(message) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
429
|
+
this.update(() => {
|
|
430
|
+
if (message) {
|
|
431
|
+
this._errors = [message];
|
|
432
|
+
} else {
|
|
433
|
+
this._errors = [];
|
|
434
|
+
}
|
|
435
|
+
this.isValid = message ? false : null;
|
|
436
|
+
});
|
|
72
437
|
}
|
|
73
438
|
get valueClear() {
|
|
74
439
|
return this._valueClear;
|
|
75
440
|
}
|
|
76
|
-
|
|
441
|
+
get isDirty() {
|
|
442
|
+
return !equalValue(this._valueClear, this._baseline);
|
|
443
|
+
}
|
|
444
|
+
commitBaseline() {
|
|
445
|
+
return this.update(() => {
|
|
446
|
+
this._baseline = copyValue(this._valueClear);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
// ========== Validation ==========
|
|
77
450
|
isRequired(form) {
|
|
78
451
|
let isRequired;
|
|
79
452
|
if (typeof this.options.isRequired === "function") {
|
|
@@ -84,107 +457,165 @@ var Field = class {
|
|
|
84
457
|
return isRequired;
|
|
85
458
|
}
|
|
86
459
|
checkValid(form) {
|
|
87
|
-
this.
|
|
88
|
-
|
|
89
|
-
if (this.
|
|
90
|
-
|
|
91
|
-
|
|
460
|
+
return this.update(() => {
|
|
461
|
+
this._errors = [];
|
|
462
|
+
if (this.isEmpty) {
|
|
463
|
+
if (this.isRequired(form)) {
|
|
464
|
+
this._errors.push(this.options.messages?.isRequired ?? "\u041F\u043E\u043B\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u0434\u043B\u044F \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F");
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
return true;
|
|
92
468
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.options.messages?.type ?? `\u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F ${this.fieldType.getTypeName()}`
|
|
98
|
-
);
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
const typeErrors = this.fieldType.validate(this._valueClear, form);
|
|
102
|
-
if (typeErrors.length > 0) {
|
|
103
|
-
this._errors.push(...typeErrors);
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
if (this.options.validate) {
|
|
107
|
-
const result = this.options.validate(this._valueClear, form);
|
|
108
|
-
if (result === false) {
|
|
109
|
-
this._errors.push(this.options.messages?.validate ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u043F\u0440\u043E\u0448\u043B\u043E \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044E");
|
|
469
|
+
if (this._valueClear === null) {
|
|
470
|
+
this._errors.push(
|
|
471
|
+
this.options.messages?.type ?? `\u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F ${this.fieldType.getTypeName()}`
|
|
472
|
+
);
|
|
110
473
|
return false;
|
|
111
474
|
}
|
|
112
|
-
|
|
113
|
-
|
|
475
|
+
const typeErrors = this.fieldType.validate(this._valueClear, form);
|
|
476
|
+
if (typeErrors.length > 0) {
|
|
477
|
+
this._errors.push(...typeErrors);
|
|
114
478
|
return false;
|
|
115
479
|
}
|
|
116
|
-
|
|
117
|
-
|
|
480
|
+
if (this.options.validate) {
|
|
481
|
+
const result = this.options.validate(this._valueClear, form);
|
|
482
|
+
if (result === false) {
|
|
483
|
+
this._errors.push(this.options.messages?.validate ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u043F\u0440\u043E\u0448\u043B\u043E \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044E");
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
if (typeof result === "string") {
|
|
487
|
+
this._errors.push(result);
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return true;
|
|
492
|
+
});
|
|
118
493
|
}
|
|
119
494
|
checkAndSetValid(form) {
|
|
120
|
-
|
|
121
|
-
|
|
495
|
+
return this.update(() => {
|
|
496
|
+
this.isValid = this.checkValid(form);
|
|
497
|
+
return this.isValid;
|
|
498
|
+
});
|
|
122
499
|
}
|
|
123
500
|
get isEmpty() {
|
|
124
501
|
let isEmpty = false;
|
|
125
502
|
if (this._rawValue === null || this._rawValue === void 0) isEmpty = true;
|
|
126
|
-
else if (this._rawValue === "") isEmpty = true;
|
|
503
|
+
else if (typeof this._rawValue === "string" && this._rawValue.trim() === "") isEmpty = true;
|
|
127
504
|
else if (Array.isArray(this._rawValue) && this._rawValue.length === 0) isEmpty = true;
|
|
128
505
|
return isEmpty;
|
|
129
506
|
}
|
|
130
|
-
// ==========
|
|
507
|
+
// ========== Helper methods ==========
|
|
131
508
|
reset() {
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
509
|
+
return this.update(() => {
|
|
510
|
+
if (this.defaultValue !== null && this.defaultValue !== void 0) {
|
|
511
|
+
this.assignRawValue(this.defaultValue);
|
|
512
|
+
} else {
|
|
513
|
+
this._rawValue = null;
|
|
514
|
+
this._valueClear = null;
|
|
515
|
+
}
|
|
516
|
+
this._errors = [];
|
|
137
517
|
this.isValid = null;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
this.isTouched = false;
|
|
518
|
+
this.isTouched = false;
|
|
519
|
+
});
|
|
141
520
|
}
|
|
142
521
|
};
|
|
143
522
|
|
|
523
|
+
// src/core/fields/arrayUtils.ts
|
|
524
|
+
function isValidArrayIndex(index, length) {
|
|
525
|
+
return Number.isInteger(index) && index >= 0 && index < length;
|
|
526
|
+
}
|
|
527
|
+
|
|
144
528
|
// src/core/fields/ArrayField.ts
|
|
145
|
-
var ArrayField = class {
|
|
529
|
+
var ArrayField = class extends ObservableNode {
|
|
146
530
|
constructor(factory, options = {}, initial = []) {
|
|
531
|
+
super();
|
|
147
532
|
this.factory = factory;
|
|
148
533
|
this._items = [];
|
|
149
534
|
this._errors = [];
|
|
150
|
-
this.
|
|
535
|
+
this._baseline = [];
|
|
536
|
+
this._hasValidationResult = false;
|
|
537
|
+
this._isTouched = false;
|
|
151
538
|
this.options = {
|
|
152
539
|
isRequired: true,
|
|
153
540
|
messages: {},
|
|
154
541
|
...options
|
|
155
542
|
};
|
|
156
|
-
this._items = initial.map((
|
|
157
|
-
this.checkValid();
|
|
543
|
+
this._items = initial.map((value) => this.factory(value));
|
|
544
|
+
this.validateAfterMutation(() => this.checkValid());
|
|
545
|
+
this.commitBaseline();
|
|
546
|
+
}
|
|
547
|
+
captureState() {
|
|
548
|
+
return {
|
|
549
|
+
itemsValue: snapshotValue(this.itemsValue),
|
|
550
|
+
items: this._items.map((item) => item.getSnapshot()),
|
|
551
|
+
errors: [...this._errors],
|
|
552
|
+
isValid: this.isValid,
|
|
553
|
+
isTouched: this.isTouched,
|
|
554
|
+
isDirty: this.isDirty
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
stateChildren() {
|
|
558
|
+
return this._items;
|
|
559
|
+
}
|
|
560
|
+
get isTouched() {
|
|
561
|
+
return this._isTouched;
|
|
562
|
+
}
|
|
563
|
+
set isTouched(value) {
|
|
564
|
+
this.update(() => {
|
|
565
|
+
this._isTouched = value;
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
get isDirty() {
|
|
569
|
+
return !equalValue(this.itemsValue, this._baseline) || this._items.some((child) => child.isDirty);
|
|
570
|
+
}
|
|
571
|
+
commitBaseline() {
|
|
572
|
+
return this.update(() => {
|
|
573
|
+
for (const child of this._items) child.commitBaseline();
|
|
574
|
+
this._baseline = copyValue(this.itemsValue);
|
|
575
|
+
});
|
|
158
576
|
}
|
|
159
577
|
// ===== value =====
|
|
160
578
|
set itemsValue(items) {
|
|
161
|
-
this.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
579
|
+
this.setItemsValue(items);
|
|
580
|
+
}
|
|
581
|
+
setItemsValue(items, validate = true) {
|
|
582
|
+
return this.update(() => {
|
|
583
|
+
this._items = [];
|
|
584
|
+
this.error = null;
|
|
585
|
+
for (const item of items) {
|
|
586
|
+
this.add(item, false);
|
|
587
|
+
}
|
|
588
|
+
if (validate) this.validateAfterMutation(() => this.checkValid());
|
|
589
|
+
});
|
|
166
590
|
}
|
|
167
591
|
get itemsValue() {
|
|
168
|
-
return this._items.map((
|
|
592
|
+
return this._items.map((item) => item.valueClear);
|
|
169
593
|
}
|
|
170
594
|
get items() {
|
|
171
595
|
return this._items;
|
|
172
596
|
}
|
|
173
597
|
// ===== actions =====
|
|
174
|
-
add(value, force_check) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
598
|
+
add(value, force_check = true) {
|
|
599
|
+
return this.update(() => {
|
|
600
|
+
const field = this.factory(value);
|
|
601
|
+
this.isTouched = true;
|
|
602
|
+
this._items.push(field);
|
|
603
|
+
if (force_check) this.validateAfterMutation(() => this.checkValid());
|
|
604
|
+
return field;
|
|
605
|
+
});
|
|
180
606
|
}
|
|
181
607
|
remove(index) {
|
|
182
|
-
this.
|
|
183
|
-
|
|
608
|
+
return this.update(() => {
|
|
609
|
+
if (!isValidArrayIndex(index, this._items.length)) return;
|
|
610
|
+
this._items.splice(index, 1);
|
|
611
|
+
this.validateAfterMutation(() => this.checkValid());
|
|
612
|
+
});
|
|
184
613
|
}
|
|
185
614
|
clear() {
|
|
186
|
-
this.
|
|
187
|
-
|
|
615
|
+
return this.update(() => {
|
|
616
|
+
this._items = [];
|
|
617
|
+
this.validateAfterMutation(() => this.checkValid());
|
|
618
|
+
});
|
|
188
619
|
}
|
|
189
620
|
// ===== validation =====
|
|
190
621
|
isRequired(form) {
|
|
@@ -197,123 +628,186 @@ var ArrayField = class {
|
|
|
197
628
|
return isRequired;
|
|
198
629
|
}
|
|
199
630
|
checkValid(form) {
|
|
200
|
-
this.
|
|
201
|
-
|
|
202
|
-
this._errors
|
|
203
|
-
|
|
204
|
-
);
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
if (this.options.minItems != null && this._items.length < this.options.minItems) {
|
|
208
|
-
this._errors.push(
|
|
209
|
-
this.options.messages?.minItems ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u044C\u0448\u0435 ${this.options.minItems}`
|
|
210
|
-
);
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
if (this.options.maxItems != null && this._items.length > this.options.maxItems) {
|
|
214
|
-
this._errors.push(
|
|
215
|
-
this.options.messages?.maxItems ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${this.options.maxItems}`
|
|
216
|
-
);
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
let valid = true;
|
|
220
|
-
for (const field of this._items) {
|
|
221
|
-
const ok = field.checkAndSetValid(form);
|
|
222
|
-
if (!ok) valid = false;
|
|
223
|
-
}
|
|
224
|
-
if (this.options.validate) {
|
|
225
|
-
const values = this._items.map((i) => i.valueClear);
|
|
226
|
-
const result = this.options.validate(values, form);
|
|
227
|
-
if (result === false) {
|
|
631
|
+
return this.update(() => {
|
|
632
|
+
this._hasValidationResult = true;
|
|
633
|
+
this._errors = [];
|
|
634
|
+
if (this.isRequired(form) && this._items.length === 0) {
|
|
228
635
|
this._errors.push(
|
|
229
|
-
this.options.messages?.
|
|
636
|
+
this.options.messages?.isRequired ?? "\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0445\u043E\u0442\u044F \u0431\u044B \u043E\u0434\u0438\u043D \u044D\u043B\u0435\u043C\u0435\u043D\u0442"
|
|
230
637
|
);
|
|
231
638
|
return false;
|
|
232
639
|
}
|
|
233
|
-
if (
|
|
234
|
-
this._errors.push(
|
|
640
|
+
if (this.options.minItems != null && this._items.length < this.options.minItems) {
|
|
641
|
+
this._errors.push(
|
|
642
|
+
this.options.messages?.minItems ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u044C\u0448\u0435 ${this.options.minItems}`
|
|
643
|
+
);
|
|
235
644
|
return false;
|
|
236
645
|
}
|
|
237
|
-
|
|
238
|
-
|
|
646
|
+
if (this.options.maxItems != null && this._items.length > this.options.maxItems) {
|
|
647
|
+
this._errors.push(
|
|
648
|
+
this.options.messages?.maxItems ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${this.options.maxItems}`
|
|
649
|
+
);
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
let valid = true;
|
|
653
|
+
for (const field of this._items) {
|
|
654
|
+
const ok = field.checkAndSetValid(form);
|
|
655
|
+
if (!ok) valid = false;
|
|
656
|
+
}
|
|
657
|
+
if (this.options.validate) {
|
|
658
|
+
const values = this._items.map((item) => item.valueClear);
|
|
659
|
+
const result = this.options.validate(values, form);
|
|
660
|
+
if (result === false) {
|
|
661
|
+
this._errors.push(
|
|
662
|
+
this.options.messages?.validate ?? "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438"
|
|
663
|
+
);
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
if (typeof result === "string") {
|
|
667
|
+
this._errors.push(result);
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return valid && this._errors.length === 0;
|
|
672
|
+
});
|
|
239
673
|
}
|
|
240
674
|
checkAndSetValid(form) {
|
|
241
675
|
return this.checkValid(form);
|
|
242
676
|
}
|
|
243
677
|
get isValid() {
|
|
678
|
+
if (!this._hasValidationResult) return false;
|
|
244
679
|
if (this._errors.length > 0) return false;
|
|
245
|
-
return this._items.every((
|
|
680
|
+
return this._items.every((item) => item.isValid === true);
|
|
246
681
|
}
|
|
247
682
|
get error() {
|
|
248
683
|
return this._errors[0] ?? "";
|
|
249
684
|
}
|
|
250
685
|
set error(message) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
686
|
+
this.update(() => {
|
|
687
|
+
if (message) {
|
|
688
|
+
this._errors = [message];
|
|
689
|
+
this._hasValidationResult = true;
|
|
690
|
+
} else {
|
|
691
|
+
this._errors = [];
|
|
692
|
+
this._hasValidationResult = false;
|
|
693
|
+
}
|
|
694
|
+
});
|
|
256
695
|
}
|
|
257
696
|
get errors() {
|
|
258
697
|
return this._errors;
|
|
259
698
|
}
|
|
260
699
|
reset() {
|
|
261
|
-
this.
|
|
262
|
-
|
|
263
|
-
|
|
700
|
+
return this.update(() => {
|
|
701
|
+
this._items.forEach((item) => item.reset());
|
|
702
|
+
this._errors = [];
|
|
703
|
+
this._hasValidationResult = false;
|
|
704
|
+
this.isTouched = false;
|
|
705
|
+
});
|
|
264
706
|
}
|
|
265
707
|
};
|
|
266
708
|
|
|
267
709
|
// src/core/fields/ArrayFormField.ts
|
|
268
|
-
var ArrayFormField = class {
|
|
710
|
+
var ArrayFormField = class extends ObservableNode {
|
|
269
711
|
constructor(FormConstructor, options = {}, initial = []) {
|
|
712
|
+
super();
|
|
270
713
|
this.FormConstructor = FormConstructor;
|
|
271
714
|
this.options = options;
|
|
272
715
|
this._forms = [];
|
|
273
716
|
this._errors = [];
|
|
274
|
-
this.
|
|
717
|
+
this._baseline = [];
|
|
718
|
+
this._hasValidationResult = false;
|
|
719
|
+
this._isTouched = false;
|
|
275
720
|
this._forms = initial.map((data) => {
|
|
276
721
|
const form = new this.FormConstructor();
|
|
277
|
-
form.
|
|
722
|
+
form.setFields(data, false);
|
|
278
723
|
return form;
|
|
279
724
|
});
|
|
280
|
-
this.checkValid();
|
|
725
|
+
this.validateAfterMutation(() => this.checkValid());
|
|
726
|
+
this.commitBaseline();
|
|
727
|
+
}
|
|
728
|
+
captureState() {
|
|
729
|
+
return {
|
|
730
|
+
formsFieldsValue: snapshotValue(this.formsFieldsValue),
|
|
731
|
+
forms: this._forms.map((form) => form.getSnapshot()),
|
|
732
|
+
errors: [...this._errors],
|
|
733
|
+
isValid: this.isValid,
|
|
734
|
+
isTouched: this.isTouched,
|
|
735
|
+
isDirty: this.isDirty
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
stateChildren() {
|
|
739
|
+
return this._forms;
|
|
740
|
+
}
|
|
741
|
+
get isTouched() {
|
|
742
|
+
return this._isTouched;
|
|
743
|
+
}
|
|
744
|
+
set isTouched(value) {
|
|
745
|
+
this.update(() => {
|
|
746
|
+
this._isTouched = value;
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
get isDirty() {
|
|
750
|
+
return !equalValue(this.formsFieldsValue, this._baseline) || this._forms.some((child) => child.isDirty);
|
|
751
|
+
}
|
|
752
|
+
commitBaseline() {
|
|
753
|
+
return this.update(() => {
|
|
754
|
+
for (const child of this._forms) child.commitBaseline();
|
|
755
|
+
this._baseline = copyValue(this.formsFieldsValue);
|
|
756
|
+
});
|
|
281
757
|
}
|
|
282
758
|
// ===== value =====
|
|
283
759
|
set forms(forms) {
|
|
284
|
-
this.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
760
|
+
this.setForms(forms);
|
|
761
|
+
}
|
|
762
|
+
setForms(forms, validate = true) {
|
|
763
|
+
return this.update(() => {
|
|
764
|
+
this._forms = [];
|
|
765
|
+
for (const formData of forms) {
|
|
766
|
+
this.add(formData, false);
|
|
767
|
+
}
|
|
768
|
+
if (validate) this.validateAfterMutation(() => this.checkValid());
|
|
769
|
+
});
|
|
289
770
|
}
|
|
290
771
|
get forms() {
|
|
291
772
|
return this._forms;
|
|
292
773
|
}
|
|
293
774
|
get formsFieldsValue() {
|
|
294
|
-
return this._forms.map((
|
|
775
|
+
return this._forms.map((form) => form.fieldsValue);
|
|
295
776
|
}
|
|
296
777
|
// ===== actions =====
|
|
297
|
-
add(data, force_check) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
778
|
+
add(data, force_check = true) {
|
|
779
|
+
return this.update(() => {
|
|
780
|
+
const form = new this.FormConstructor();
|
|
781
|
+
if (data !== void 0 && data !== null) {
|
|
782
|
+
form.setFields(data, false);
|
|
783
|
+
}
|
|
784
|
+
form.commitBaseline();
|
|
785
|
+
this.isTouched = true;
|
|
786
|
+
this._forms.push(form);
|
|
787
|
+
if (force_check) this.validateAfterMutation(() => this.checkValid());
|
|
788
|
+
return form;
|
|
789
|
+
});
|
|
304
790
|
}
|
|
305
791
|
remove(index) {
|
|
306
|
-
this.
|
|
307
|
-
|
|
792
|
+
return this.update(() => {
|
|
793
|
+
if (!isValidArrayIndex(index, this._forms.length)) return;
|
|
794
|
+
this._forms.splice(index, 1);
|
|
795
|
+
this.validateAfterMutation(() => this.checkValid());
|
|
796
|
+
});
|
|
308
797
|
}
|
|
309
798
|
clear() {
|
|
310
|
-
this.
|
|
311
|
-
|
|
799
|
+
return this.update(() => {
|
|
800
|
+
this._forms = [];
|
|
801
|
+
this.validateAfterMutation(() => this.checkValid());
|
|
802
|
+
});
|
|
312
803
|
}
|
|
313
804
|
reset() {
|
|
314
|
-
this.
|
|
315
|
-
|
|
316
|
-
|
|
805
|
+
return this.update(() => {
|
|
806
|
+
this._forms.forEach((form) => form.reset());
|
|
807
|
+
this._errors = [];
|
|
808
|
+
this._hasValidationResult = false;
|
|
809
|
+
this.isTouched = false;
|
|
810
|
+
});
|
|
317
811
|
}
|
|
318
812
|
// ===== validation =====
|
|
319
813
|
isRequired(form) {
|
|
@@ -326,51 +820,55 @@ var ArrayFormField = class {
|
|
|
326
820
|
return isRequired;
|
|
327
821
|
}
|
|
328
822
|
checkValid(form) {
|
|
329
|
-
this.
|
|
330
|
-
|
|
331
|
-
this._errors
|
|
332
|
-
|
|
333
|
-
);
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
if (this.options.minForms != null && this._forms.length < this.options.minForms) {
|
|
337
|
-
this._errors.push(
|
|
338
|
-
this.options.messages?.minForms ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u044C\u0448\u0435 ${this.options.minForms}`
|
|
339
|
-
);
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
if (this.options.maxForms != null && this._forms.length > this.options.maxForms) {
|
|
343
|
-
this._errors.push(
|
|
344
|
-
this.options.messages?.maxForms ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${this.options.maxForms}`
|
|
345
|
-
);
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
let valid = true;
|
|
349
|
-
for (const form2 of this.forms) {
|
|
350
|
-
const ok = form2.checkValid(form2);
|
|
351
|
-
if (!ok) valid = false;
|
|
352
|
-
}
|
|
353
|
-
if (this.options.validate) {
|
|
354
|
-
const result = this.options.validate(this.forms, form);
|
|
355
|
-
if (result === false) {
|
|
823
|
+
return this.update(() => {
|
|
824
|
+
this._hasValidationResult = true;
|
|
825
|
+
this._errors = [];
|
|
826
|
+
if (this.isRequired(form) && this._forms.length === 0) {
|
|
356
827
|
this._errors.push(
|
|
357
|
-
this.options.messages?.
|
|
828
|
+
this.options.messages?.isRequired ?? "\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0445\u043E\u0442\u044F \u0431\u044B \u043E\u0434\u0438\u043D \u044D\u043B\u0435\u043C\u0435\u043D\u0442"
|
|
358
829
|
);
|
|
359
830
|
return false;
|
|
360
831
|
}
|
|
361
|
-
if (
|
|
362
|
-
this._errors.push(
|
|
832
|
+
if (this.options.minForms != null && this._forms.length < this.options.minForms) {
|
|
833
|
+
this._errors.push(
|
|
834
|
+
this.options.messages?.minForms ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u044C\u0448\u0435 ${this.options.minForms}`
|
|
835
|
+
);
|
|
363
836
|
return false;
|
|
364
837
|
}
|
|
365
|
-
|
|
366
|
-
|
|
838
|
+
if (this.options.maxForms != null && this._forms.length > this.options.maxForms) {
|
|
839
|
+
this._errors.push(
|
|
840
|
+
this.options.messages?.maxForms ?? `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${this.options.maxForms}`
|
|
841
|
+
);
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
let valid = true;
|
|
845
|
+
for (const nestedForm of this._forms) {
|
|
846
|
+
const ok = nestedForm.checkValid(nestedForm);
|
|
847
|
+
if (!ok) valid = false;
|
|
848
|
+
}
|
|
849
|
+
if (this.options.validate) {
|
|
850
|
+
const result = this.options.validate(this._forms, form);
|
|
851
|
+
if (result === false) {
|
|
852
|
+
this._errors.push(
|
|
853
|
+
this.options.messages?.validate ?? "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438"
|
|
854
|
+
);
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
if (typeof result === "string") {
|
|
858
|
+
this._errors.push(result);
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return valid && this._errors.length === 0;
|
|
863
|
+
});
|
|
367
864
|
}
|
|
368
865
|
checkAndSetValid(form) {
|
|
369
866
|
return this.checkValid(form);
|
|
370
867
|
}
|
|
371
868
|
get isValid() {
|
|
869
|
+
if (!this._hasValidationResult) return false;
|
|
372
870
|
if (this._errors.length > 0) return false;
|
|
373
|
-
return this._forms.every((
|
|
871
|
+
return this._forms.every((form) => form.isValid);
|
|
374
872
|
}
|
|
375
873
|
get errors() {
|
|
376
874
|
return this._errors;
|
|
@@ -379,11 +877,15 @@ var ArrayFormField = class {
|
|
|
379
877
|
return this._errors[0] ?? "";
|
|
380
878
|
}
|
|
381
879
|
set error(message) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
880
|
+
this.update(() => {
|
|
881
|
+
if (message) {
|
|
882
|
+
this._errors = [message];
|
|
883
|
+
this._hasValidationResult = true;
|
|
884
|
+
} else {
|
|
885
|
+
this._errors = [];
|
|
886
|
+
this._hasValidationResult = false;
|
|
887
|
+
}
|
|
888
|
+
});
|
|
387
889
|
}
|
|
388
890
|
};
|
|
389
891
|
|
|
@@ -395,19 +897,19 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
395
897
|
this._valueTransformers = [];
|
|
396
898
|
}
|
|
397
899
|
/**
|
|
398
|
-
*
|
|
399
|
-
* @param errorKey -
|
|
400
|
-
* @param fieldName -
|
|
900
|
+
* Add a static rule that matches an exact error key.
|
|
901
|
+
* @param errorKey - Exact error key returned by the server.
|
|
902
|
+
* @param fieldName - Form field name or a function that resolves it dynamically.
|
|
401
903
|
*/
|
|
402
904
|
staticKey(errorKey, fieldName) {
|
|
403
905
|
this._staticRules.set(errorKey, fieldName);
|
|
404
906
|
return this;
|
|
405
907
|
}
|
|
406
908
|
/**
|
|
407
|
-
*
|
|
408
|
-
* @param pattern -
|
|
409
|
-
* @param mapping -
|
|
410
|
-
* @param options -
|
|
909
|
+
* Add a dynamic rule using a regular expression.
|
|
910
|
+
* @param pattern - Regular expression used to match an error key.
|
|
911
|
+
* @param mapping - Field name or function that derives it from the match.
|
|
912
|
+
* @param options - Additional options, such as rule priority.
|
|
411
913
|
*/
|
|
412
914
|
dynamicKey(pattern, mapping, options) {
|
|
413
915
|
this._dynamicRules.push({
|
|
@@ -419,22 +921,22 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
419
921
|
return this;
|
|
420
922
|
}
|
|
421
923
|
/**
|
|
422
|
-
*
|
|
924
|
+
* Set a handler for errors that do not match any rule.
|
|
423
925
|
*/
|
|
424
926
|
fallback(handler) {
|
|
425
927
|
this._fallbackHandler = handler;
|
|
426
928
|
return this;
|
|
427
929
|
}
|
|
428
930
|
/**
|
|
429
|
-
*
|
|
430
|
-
*
|
|
931
|
+
* Add a transformer for error values.
|
|
932
|
+
* Useful for normalizing messages, such as server responses containing arrays.
|
|
431
933
|
*/
|
|
432
934
|
transformValue(transformer) {
|
|
433
935
|
this._valueTransformers.push(transformer);
|
|
434
936
|
return this;
|
|
435
937
|
}
|
|
436
938
|
/**
|
|
437
|
-
*
|
|
939
|
+
* Create a schema from a simple mapping object.
|
|
438
940
|
*/
|
|
439
941
|
static fromObject(mapping) {
|
|
440
942
|
const schema = new _ErrorsSchema();
|
|
@@ -444,24 +946,35 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
444
946
|
return schema;
|
|
445
947
|
}
|
|
446
948
|
/**
|
|
447
|
-
*
|
|
949
|
+
* Create a schema for an array of similarly structured forms.
|
|
448
950
|
*/
|
|
449
951
|
static forArray(itemSchema, arrayFieldName, indexExtractor) {
|
|
450
952
|
const schema = new _ErrorsSchema();
|
|
953
|
+
for (const transformer of itemSchema._valueTransformers) {
|
|
954
|
+
schema.transformValue(transformer);
|
|
955
|
+
}
|
|
956
|
+
const escapedArrayFieldName = arrayFieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
957
|
+
const indexPattern = indexExtractor ? "(.+?)" : "(?:\\[(\\d+)\\]|\\.(\\d+))";
|
|
451
958
|
schema.dynamicKey(
|
|
452
|
-
new RegExp(`^${
|
|
453
|
-
(match) => {
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
|
|
959
|
+
new RegExp(`^${escapedArrayFieldName}${indexPattern}\\.(.+)$`),
|
|
960
|
+
(match, value) => {
|
|
961
|
+
const sourceKey = match[0];
|
|
962
|
+
const index = indexExtractor ? indexExtractor(sourceKey) : Number(match[1] ?? match[2]);
|
|
963
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
964
|
+
return "";
|
|
965
|
+
}
|
|
966
|
+
const fieldPath = match[indexExtractor ? 2 : 3];
|
|
967
|
+
const itemMapping = itemSchema.findMapping(fieldPath, value);
|
|
968
|
+
const mappedFieldPath = itemMapping ? typeof itemMapping === "function" ? itemMapping(value) : itemMapping : fieldPath;
|
|
969
|
+
return `${arrayFieldName}.${index}.${mappedFieldPath}`;
|
|
457
970
|
},
|
|
458
971
|
{ priority: 100 }
|
|
459
972
|
);
|
|
460
973
|
return schema;
|
|
461
974
|
}
|
|
462
975
|
/**
|
|
463
|
-
*
|
|
464
|
-
*
|
|
976
|
+
* Parse errors while applying configured value transformers.
|
|
977
|
+
* Supports nested structures using dot notation.
|
|
465
978
|
*/
|
|
466
979
|
parseErrors(errors, prefix = "") {
|
|
467
980
|
const result = {};
|
|
@@ -489,17 +1002,12 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
489
1002
|
} else {
|
|
490
1003
|
result[mapping] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
491
1004
|
}
|
|
492
|
-
} else if (this._fallbackHandler) {
|
|
493
|
-
const fieldName = this._fallbackHandler(fullKey, transformedValue);
|
|
494
|
-
if (fieldName) {
|
|
495
|
-
result[fieldName] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
496
|
-
}
|
|
497
1005
|
}
|
|
498
1006
|
}
|
|
499
1007
|
return result;
|
|
500
1008
|
}
|
|
501
1009
|
/**
|
|
502
|
-
*
|
|
1010
|
+
* Parse server errors and return detailed metadata.
|
|
503
1011
|
*/
|
|
504
1012
|
parseErrorsDetailed(errors, prefix = "") {
|
|
505
1013
|
const result = {};
|
|
@@ -531,7 +1039,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
531
1039
|
return result;
|
|
532
1040
|
}
|
|
533
1041
|
/**
|
|
534
|
-
*
|
|
1042
|
+
* Create a schema for a nested form.
|
|
535
1043
|
*/
|
|
536
1044
|
nest(prefix) {
|
|
537
1045
|
const nestedSchema = new _ErrorsSchema();
|
|
@@ -556,7 +1064,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
556
1064
|
return nestedSchema;
|
|
557
1065
|
}
|
|
558
1066
|
/**
|
|
559
|
-
*
|
|
1067
|
+
* Clone the current schema.
|
|
560
1068
|
*/
|
|
561
1069
|
clone() {
|
|
562
1070
|
const cloned = new _ErrorsSchema();
|
|
@@ -571,7 +1079,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
571
1079
|
return cloned;
|
|
572
1080
|
}
|
|
573
1081
|
/**
|
|
574
|
-
*
|
|
1082
|
+
* Merge the current schema with another schema.
|
|
575
1083
|
*/
|
|
576
1084
|
merge(other) {
|
|
577
1085
|
const merged = this.clone();
|
|
@@ -598,7 +1106,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
598
1106
|
for (const rule of this._dynamicRules) {
|
|
599
1107
|
const match = key.match(rule.pattern);
|
|
600
1108
|
if (match) {
|
|
601
|
-
const fieldName = typeof rule.mapping === "function" ? rule.mapping(match) : rule.mapping;
|
|
1109
|
+
const fieldName = typeof rule.mapping === "function" ? rule.mapping(match, value) : rule.mapping;
|
|
602
1110
|
return fieldName ? { fieldName, matchedBy: "dynamic" } : null;
|
|
603
1111
|
}
|
|
604
1112
|
}
|
|
@@ -616,7 +1124,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
616
1124
|
const match = key.match(rule.pattern);
|
|
617
1125
|
if (match) {
|
|
618
1126
|
if (typeof rule.mapping === "function") {
|
|
619
|
-
const fieldName = rule.mapping(match);
|
|
1127
|
+
const fieldName = rule.mapping(match, value);
|
|
620
1128
|
return fieldName;
|
|
621
1129
|
}
|
|
622
1130
|
return rule.mapping;
|
|
@@ -632,11 +1140,29 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
632
1140
|
var DefaultErrorsSchema = new ErrorsSchema().fallback((key) => key);
|
|
633
1141
|
|
|
634
1142
|
// src/core/Form.ts
|
|
635
|
-
var Form = class {
|
|
1143
|
+
var Form = class extends ObservableNode {
|
|
636
1144
|
constructor() {
|
|
1145
|
+
super(...arguments);
|
|
637
1146
|
this._globalErrors = {};
|
|
638
1147
|
this.errorsShema = DefaultErrorsSchema;
|
|
639
1148
|
}
|
|
1149
|
+
captureState() {
|
|
1150
|
+
return {
|
|
1151
|
+
fieldsValue: snapshotValue(this.fieldsValue),
|
|
1152
|
+
allErrors: this.allErrors,
|
|
1153
|
+
globalErrors: this.globalErrors,
|
|
1154
|
+
fields: Object.fromEntries(Object.entries(this._fields).map(([key, field]) => [key, field.getSnapshot()])),
|
|
1155
|
+
isValid: this.isValid,
|
|
1156
|
+
isTouched: this.isTouched,
|
|
1157
|
+
isDirty: this.isDirty
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
stateChildren() {
|
|
1161
|
+
return Object.values(this._fields);
|
|
1162
|
+
}
|
|
1163
|
+
batch(callback) {
|
|
1164
|
+
return this.batchUpdate(callback, () => this.checkValid());
|
|
1165
|
+
}
|
|
640
1166
|
get _fields() {
|
|
641
1167
|
const fields = {};
|
|
642
1168
|
for (const key of Object.keys(this)) {
|
|
@@ -648,15 +1174,17 @@ var Form = class {
|
|
|
648
1174
|
return fields;
|
|
649
1175
|
}
|
|
650
1176
|
checkValid(form) {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
1177
|
+
return this.update(() => {
|
|
1178
|
+
let isFormValid = true;
|
|
1179
|
+
const fields = this._fields;
|
|
1180
|
+
for (const key of Object.keys(fields)) {
|
|
1181
|
+
const field = fields[key];
|
|
1182
|
+
if (!field.checkAndSetValid(form ?? this)) {
|
|
1183
|
+
isFormValid = false;
|
|
1184
|
+
}
|
|
657
1185
|
}
|
|
658
|
-
|
|
659
|
-
|
|
1186
|
+
return isFormValid && Object.keys(this._globalErrors).length === 0;
|
|
1187
|
+
});
|
|
660
1188
|
}
|
|
661
1189
|
get fieldsValue() {
|
|
662
1190
|
const data = {};
|
|
@@ -673,16 +1201,22 @@ var Form = class {
|
|
|
673
1201
|
return data;
|
|
674
1202
|
}
|
|
675
1203
|
set fields(data) {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1204
|
+
this.setFields(data);
|
|
1205
|
+
}
|
|
1206
|
+
setFields(data, validate = true) {
|
|
1207
|
+
return this.update(() => {
|
|
1208
|
+
const fields = this._fields;
|
|
1209
|
+
for (const key of Object.keys(data)) {
|
|
1210
|
+
const field = fields[key];
|
|
1211
|
+
if (field) {
|
|
1212
|
+
if (field instanceof Field) field.setRawValue(data[key], false);
|
|
1213
|
+
else if (field instanceof ArrayField) field.setItemsValue(data[key], false);
|
|
1214
|
+
else if (field instanceof ArrayFormField) field.setForms(data[key], false);
|
|
1215
|
+
else console.error(`Invalid field "${key}" type`);
|
|
1216
|
+
}
|
|
684
1217
|
}
|
|
685
|
-
|
|
1218
|
+
if (validate) this.validateAfterMutation(() => this.checkValid());
|
|
1219
|
+
});
|
|
686
1220
|
}
|
|
687
1221
|
get allErrors() {
|
|
688
1222
|
const errors = {};
|
|
@@ -697,7 +1231,7 @@ var Form = class {
|
|
|
697
1231
|
} else {
|
|
698
1232
|
const fieldErrors = field.errors;
|
|
699
1233
|
if (fieldErrors.length > 0) {
|
|
700
|
-
errors[key] = fieldErrors;
|
|
1234
|
+
errors[key] = [...fieldErrors];
|
|
701
1235
|
}
|
|
702
1236
|
}
|
|
703
1237
|
}
|
|
@@ -710,7 +1244,7 @@ var Form = class {
|
|
|
710
1244
|
return errors;
|
|
711
1245
|
}
|
|
712
1246
|
/**
|
|
713
|
-
*
|
|
1247
|
+
* Recursively collect all errors from an ArrayFormField.
|
|
714
1248
|
*/
|
|
715
1249
|
collectArrayFormErrors(arrayField) {
|
|
716
1250
|
const allErrors = [];
|
|
@@ -730,19 +1264,34 @@ var Form = class {
|
|
|
730
1264
|
return { ...this._globalErrors };
|
|
731
1265
|
}
|
|
732
1266
|
set errors(errors) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1267
|
+
this.update(() => {
|
|
1268
|
+
this.clearErrorsRecursively();
|
|
1269
|
+
const fields = this._fields;
|
|
1270
|
+
const parsedErrors = this.errorsShema.parseErrors(errors);
|
|
1271
|
+
for (const [fieldName, errorMessage] of Object.entries(parsedErrors)) {
|
|
1272
|
+
this.applyErrorToField(fieldName, errorMessage, fields, this);
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
clearErrorsRecursively() {
|
|
1277
|
+
this.update(() => {
|
|
1278
|
+
this._globalErrors = {};
|
|
1279
|
+
for (const field of Object.values(this._fields)) {
|
|
1280
|
+
if (field instanceof Field) {
|
|
1281
|
+
field.error = null;
|
|
1282
|
+
} else if (field instanceof ArrayField) {
|
|
1283
|
+
field.error = null;
|
|
1284
|
+
for (const item of field.items) item.error = null;
|
|
1285
|
+
} else if (field instanceof ArrayFormField) {
|
|
1286
|
+
field.error = null;
|
|
1287
|
+
for (const nestedForm of field.forms) nestedForm.clearErrorsRecursively();
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
742
1291
|
}
|
|
743
1292
|
/**
|
|
744
|
-
*
|
|
745
|
-
*
|
|
1293
|
+
* Apply an error to the corresponding field.
|
|
1294
|
+
* Supports nested fields using dot notation.
|
|
746
1295
|
*/
|
|
747
1296
|
applyErrorToField(fieldPath, errorMessage, fields, targetForm) {
|
|
748
1297
|
if (!targetForm) {
|
|
@@ -790,17 +1339,31 @@ var Form = class {
|
|
|
790
1339
|
}
|
|
791
1340
|
}
|
|
792
1341
|
setGlobalError(key, message) {
|
|
793
|
-
this.
|
|
1342
|
+
return this.update(() => {
|
|
1343
|
+
this._globalErrors[key] = message;
|
|
1344
|
+
});
|
|
794
1345
|
}
|
|
795
1346
|
clearGlobalErrors() {
|
|
796
|
-
this.
|
|
1347
|
+
return this.update(() => {
|
|
1348
|
+
this._globalErrors = {};
|
|
1349
|
+
});
|
|
797
1350
|
}
|
|
798
1351
|
reset() {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1352
|
+
return this.update(() => {
|
|
1353
|
+
const fields = this._fields;
|
|
1354
|
+
for (const key of Object.keys(fields)) {
|
|
1355
|
+
fields[key].reset();
|
|
1356
|
+
}
|
|
1357
|
+
this._globalErrors = {};
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
get isDirty() {
|
|
1361
|
+
return Object.values(this._fields).some((field) => field.isDirty);
|
|
1362
|
+
}
|
|
1363
|
+
commitBaseline() {
|
|
1364
|
+
return this.update(() => {
|
|
1365
|
+
for (const field of Object.values(this._fields)) field.commitBaseline();
|
|
1366
|
+
});
|
|
804
1367
|
}
|
|
805
1368
|
get isTouched() {
|
|
806
1369
|
const fields = this._fields;
|
|
@@ -809,7 +1372,7 @@ var Form = class {
|
|
|
809
1372
|
}
|
|
810
1373
|
return false;
|
|
811
1374
|
}
|
|
812
|
-
//
|
|
1375
|
+
// Check whether the form is valid.
|
|
813
1376
|
get isValid() {
|
|
814
1377
|
const fields = this._fields;
|
|
815
1378
|
for (const key of Object.keys(fields)) {
|
|
@@ -817,32 +1380,50 @@ var Form = class {
|
|
|
817
1380
|
}
|
|
818
1381
|
return Object.keys(this._globalErrors).length === 0;
|
|
819
1382
|
}
|
|
820
|
-
//
|
|
1383
|
+
// Serialize form values for multipart requests.
|
|
821
1384
|
toFormData() {
|
|
822
1385
|
const formData = new FormData();
|
|
823
1386
|
const data = this.fieldsValue;
|
|
824
1387
|
for (const [key, value] of Object.entries(data)) {
|
|
825
|
-
if (value
|
|
1388
|
+
if (value === null || value === void 0) continue;
|
|
1389
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) {
|
|
1390
|
+
formData.append(key, value);
|
|
1391
|
+
} else if (value instanceof Date) {
|
|
826
1392
|
formData.append(key, value.toISOString());
|
|
827
|
-
} else if (
|
|
1393
|
+
} else if (typeof value === "object") {
|
|
1394
|
+
const serialized = JSON.stringify(value);
|
|
1395
|
+
if (serialized !== void 0) formData.append(key, serialized);
|
|
1396
|
+
} else {
|
|
828
1397
|
formData.append(key, String(value));
|
|
829
1398
|
}
|
|
830
1399
|
}
|
|
831
1400
|
return formData;
|
|
832
1401
|
}
|
|
833
|
-
//
|
|
1402
|
+
// Get all values in JSON-compatible form.
|
|
834
1403
|
toJSON() {
|
|
835
|
-
const data = this.fieldsValue;
|
|
836
1404
|
const json = {};
|
|
837
|
-
for (const [key, value] of Object.entries(
|
|
838
|
-
|
|
839
|
-
json[key] = value.toISOString();
|
|
840
|
-
} else {
|
|
841
|
-
json[key] = value;
|
|
842
|
-
}
|
|
1405
|
+
for (const [key, value] of Object.entries(this.fieldsValue)) {
|
|
1406
|
+
json[key] = this.serializeJsonValue(value);
|
|
843
1407
|
}
|
|
844
1408
|
return json;
|
|
845
1409
|
}
|
|
1410
|
+
serializeJsonValue(value) {
|
|
1411
|
+
if (value instanceof Date) {
|
|
1412
|
+
return value.toISOString();
|
|
1413
|
+
}
|
|
1414
|
+
if (Array.isArray(value)) {
|
|
1415
|
+
return value.map((item) => this.serializeJsonValue(item));
|
|
1416
|
+
}
|
|
1417
|
+
if (value !== null && typeof value === "object") {
|
|
1418
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1419
|
+
if (prototype === Object.prototype || prototype === null) {
|
|
1420
|
+
return Object.fromEntries(
|
|
1421
|
+
Object.entries(value).map(([key, nestedValue]) => [key, this.serializeJsonValue(nestedValue)])
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
return value;
|
|
1426
|
+
}
|
|
846
1427
|
};
|
|
847
1428
|
|
|
848
1429
|
// src/core/plugins/FieldPlugin.ts
|
|
@@ -862,13 +1443,13 @@ var MaskPlugin = class extends BasePlugin {
|
|
|
862
1443
|
this.maskParts = [];
|
|
863
1444
|
this.config = config;
|
|
864
1445
|
}
|
|
865
|
-
init(
|
|
1446
|
+
init(_field) {
|
|
866
1447
|
this.parseMask();
|
|
867
1448
|
}
|
|
868
|
-
toRawValue(value,
|
|
1449
|
+
toRawValue(value, _field) {
|
|
869
1450
|
return this.applyMask(value);
|
|
870
1451
|
}
|
|
871
|
-
toValueClear(value,
|
|
1452
|
+
toValueClear(value, _field) {
|
|
872
1453
|
return this.removeMask(value);
|
|
873
1454
|
}
|
|
874
1455
|
parseMask() {
|
|
@@ -1067,8 +1648,12 @@ var StringType = class extends FieldType {
|
|
|
1067
1648
|
this.options.messages?.maxLength ?? `\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430 ${this.options.maxLength} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`
|
|
1068
1649
|
);
|
|
1069
1650
|
}
|
|
1070
|
-
if (this.options.pattern != null
|
|
1071
|
-
|
|
1651
|
+
if (this.options.pattern != null) {
|
|
1652
|
+
const { source, flags } = this.options.pattern;
|
|
1653
|
+
const stablePattern = new RegExp(source, flags.replace(/[gy]/g, ""));
|
|
1654
|
+
if (!stablePattern.test(value)) {
|
|
1655
|
+
errors.push(this.options.messages?.pattern ?? "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442");
|
|
1656
|
+
}
|
|
1072
1657
|
}
|
|
1073
1658
|
return errors;
|
|
1074
1659
|
}
|
|
@@ -1081,15 +1666,21 @@ var StringType = class extends FieldType {
|
|
|
1081
1666
|
var NumberType = class extends FieldType {
|
|
1082
1667
|
cast(rawValue) {
|
|
1083
1668
|
if (rawValue === null || rawValue === void 0) return null;
|
|
1084
|
-
if (typeof rawValue === "number"
|
|
1669
|
+
if (typeof rawValue === "number") return Number.isFinite(rawValue) ? rawValue : null;
|
|
1085
1670
|
if (typeof rawValue === "string") {
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1671
|
+
const normalized = rawValue.trim();
|
|
1672
|
+
if (normalized.length === 0) return null;
|
|
1673
|
+
const num = Number(normalized);
|
|
1674
|
+
return Number.isFinite(num) ? num : null;
|
|
1088
1675
|
}
|
|
1089
1676
|
return null;
|
|
1090
1677
|
}
|
|
1091
1678
|
validate(value) {
|
|
1092
1679
|
const errors = [];
|
|
1680
|
+
if (!Number.isFinite(value)) {
|
|
1681
|
+
errors.push(this.options.messages?.invalid ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043A\u043E\u043D\u0435\u0447\u043D\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C");
|
|
1682
|
+
return errors;
|
|
1683
|
+
}
|
|
1093
1684
|
if (!Number.isInteger(value)) {
|
|
1094
1685
|
errors.push(this.options.messages?.invalid ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0446\u0435\u043B\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C");
|
|
1095
1686
|
}
|
|
@@ -1134,7 +1725,7 @@ var DecimalType = class extends FieldType {
|
|
|
1134
1725
|
if (typeof rawValue === "string") {
|
|
1135
1726
|
return this.castString(rawValue);
|
|
1136
1727
|
}
|
|
1137
|
-
if (typeof rawValue === "number" &&
|
|
1728
|
+
if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
|
|
1138
1729
|
return rawValue;
|
|
1139
1730
|
}
|
|
1140
1731
|
return null;
|
|
@@ -1150,27 +1741,19 @@ var DecimalType = class extends FieldType {
|
|
|
1150
1741
|
}
|
|
1151
1742
|
validate(value) {
|
|
1152
1743
|
const errors = [];
|
|
1153
|
-
value = typeof value === "number" ?
|
|
1744
|
+
value = typeof value === "number" ? String(value) : value;
|
|
1154
1745
|
if (!this.isValidNumberString(value)) {
|
|
1155
1746
|
errors.push(this.options.messages?.invalid ?? "\u041F\u0440\u0438\u043C\u0435\u0440: 1.00");
|
|
1156
1747
|
return errors;
|
|
1157
1748
|
}
|
|
1158
|
-
const maxDigits = this.options.maxDigits
|
|
1159
|
-
const decimalPlaces = this.options.decimalPlaces
|
|
1749
|
+
const maxDigits = this.options.maxDigits ?? 8;
|
|
1750
|
+
const decimalPlaces = this.options.decimalPlaces ?? 2;
|
|
1160
1751
|
const maxIntDigits = maxDigits - decimalPlaces;
|
|
1161
1752
|
const isNegative = value.startsWith("-");
|
|
1162
1753
|
const normalizedValue = isNegative ? value.substring(1) : value;
|
|
1163
1754
|
const parts = normalizedValue.split(".");
|
|
1164
1755
|
const intPart = parts[0];
|
|
1165
1756
|
const decPart = parts.length > 1 ? parts[1] : "";
|
|
1166
|
-
if (normalizedValue.endsWith(".")) {
|
|
1167
|
-
if (intPart.length > maxIntDigits) {
|
|
1168
|
-
errors.push(
|
|
1169
|
-
this.options.messages?.maxDigits ?? `\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0437\u043D\u0430\u043A\u043E\u0432 \u043F\u0435\u0440\u0435\u0434 \u0437\u0430\u043F\u044F\u0442\u043E\u0439 ${maxIntDigits}`
|
|
1170
|
-
);
|
|
1171
|
-
}
|
|
1172
|
-
return errors;
|
|
1173
|
-
}
|
|
1174
1757
|
if (intPart.length === 0) {
|
|
1175
1758
|
errors.push(this.options.messages?.invalid ?? "\u041F\u0440\u0438\u043C\u0435\u0440: 1.00");
|
|
1176
1759
|
return errors;
|
|
@@ -1230,10 +1813,6 @@ var DecimalType = class extends FieldType {
|
|
|
1230
1813
|
if (dotCount > 1) return false;
|
|
1231
1814
|
return pattern.test(value);
|
|
1232
1815
|
}
|
|
1233
|
-
numberToString(value) {
|
|
1234
|
-
const decimalPlaces = this.options.decimalPlaces;
|
|
1235
|
-
return value.toFixed(decimalPlaces);
|
|
1236
|
-
}
|
|
1237
1816
|
getTypeName() {
|
|
1238
1817
|
return "\u0434\u0435\u0441\u044F\u0442\u0438\u0447\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E";
|
|
1239
1818
|
}
|
|
@@ -1310,7 +1889,7 @@ var DateType = class extends FieldType {
|
|
|
1310
1889
|
getTypeName() {
|
|
1311
1890
|
return this.options.isDatetime ? "\u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043C\u044F" : "\u0434\u0430\u0442\u0430";
|
|
1312
1891
|
}
|
|
1313
|
-
//
|
|
1892
|
+
// Compare dates with optional time precision.
|
|
1314
1893
|
compareDates(value, boundary, type) {
|
|
1315
1894
|
if (this.options.isDatetime) {
|
|
1316
1895
|
if (type === "min") {
|
|
@@ -1339,6 +1918,8 @@ exports.BasePlugin = BasePlugin;
|
|
|
1339
1918
|
exports.BooleanType = BooleanType;
|
|
1340
1919
|
exports.DateType = DateType;
|
|
1341
1920
|
exports.DecimalType = DecimalType;
|
|
1921
|
+
exports.DefaultErrorsSchema = DefaultErrorsSchema;
|
|
1922
|
+
exports.ErrorsSchema = ErrorsSchema;
|
|
1342
1923
|
exports.Field = Field;
|
|
1343
1924
|
exports.FieldType = FieldType;
|
|
1344
1925
|
exports.Form = Form;
|