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