@reformer/core 1.1.0-beta.8 → 2.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/behaviors/compute-from.d.ts +2 -0
- package/dist/behaviors/compute-from.js +31 -0
- package/dist/behaviors/copy-from.d.ts +2 -0
- package/dist/behaviors/copy-from.js +29 -0
- package/dist/behaviors/enable-when.d.ts +2 -0
- package/dist/behaviors/enable-when.js +25 -0
- package/dist/behaviors/reset-when.d.ts +2 -0
- package/dist/behaviors/reset-when.js +24 -0
- package/dist/behaviors/revalidate-when.d.ts +2 -0
- package/dist/behaviors/revalidate-when.js +18 -0
- package/dist/behaviors/sync-fields.d.ts +2 -0
- package/dist/behaviors/sync-fields.js +41 -0
- package/dist/behaviors/transform-value.d.ts +2 -0
- package/dist/behaviors/transform-value.js +45 -0
- package/dist/behaviors/watch-field.d.ts +2 -0
- package/dist/behaviors/watch-field.js +21 -0
- package/dist/behaviors.js +27 -20
- package/dist/core/behavior/behavior-context.d.ts +27 -13
- package/dist/core/behavior/behavior-registry.d.ts +15 -27
- package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
- package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
- package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
- package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
- package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
- package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
- package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
- package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
- package/dist/core/behavior/compose-behavior.d.ts +2 -12
- package/dist/core/behavior/index.d.ts +0 -1
- package/dist/core/behavior/types.d.ts +2 -8
- package/dist/core/factories/node-factory.d.ts +6 -29
- package/dist/core/nodes/array-node.d.ts +42 -22
- package/dist/core/nodes/field-node.d.ts +51 -26
- package/dist/core/nodes/form-node.d.ts +18 -20
- package/dist/core/nodes/group-node.d.ts +37 -212
- package/dist/core/types/deep-schema.d.ts +2 -12
- package/dist/core/types/field-path.d.ts +1 -1
- package/dist/core/types/form-context.d.ts +27 -27
- package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
- package/dist/core/types/index.d.ts +52 -6
- package/dist/core/types/validation-schema.d.ts +3 -12
- package/dist/core/utils/abstract-registry.d.ts +74 -0
- package/dist/core/utils/aggregate-signals.d.ts +71 -0
- package/dist/core/utils/create-form.d.ts +3 -20
- package/dist/core/utils/error-handler.d.ts +1 -18
- package/dist/core/utils/field-path-navigator.d.ts +1 -1
- package/dist/core/{validation → utils}/field-path.d.ts +23 -6
- package/dist/core/utils/form-observer.d.ts +176 -0
- package/dist/core/utils/form-proxy-builder.d.ts +25 -0
- package/dist/core/utils/form-submitter.d.ts +121 -0
- package/dist/core/utils/index.d.ts +10 -2
- package/dist/core/utils/registry-helpers.d.ts +0 -7
- package/dist/core/utils/safe-effect.d.ts +73 -0
- package/dist/core/utils/status-machine.d.ts +153 -0
- package/dist/core/utils/type-guards.d.ts +5 -23
- package/dist/core/utils/unique-id.d.ts +53 -0
- package/dist/core/validation/core/apply-when.d.ts +3 -9
- package/dist/core/validation/core/apply.d.ts +2 -13
- package/dist/core/validation/core/validate-async.d.ts +2 -8
- package/dist/core/validation/core/validate-tree.d.ts +0 -6
- package/dist/core/validation/core/validate.d.ts +1 -7
- package/dist/core/validation/index.d.ts +8 -2
- package/dist/core/validation/validate-form.d.ts +1 -38
- package/dist/core/validation/validation-applicator.d.ts +2 -21
- package/dist/core/validation/validation-context.d.ts +67 -28
- package/dist/core/validation/validation-registry.d.ts +11 -25
- package/dist/core/validation/validators/array-validators.d.ts +2 -12
- package/dist/core/validation/validators/date-utils.d.ts +26 -0
- package/dist/core/validation/validators/email.d.ts +2 -9
- package/dist/core/validation/validators/future-date.d.ts +35 -0
- package/dist/core/validation/validators/index.d.ts +7 -1
- package/dist/core/validation/validators/is-date.d.ts +36 -0
- package/dist/core/validation/validators/max-age.d.ts +36 -0
- package/dist/core/validation/validators/max-date.d.ts +36 -0
- package/dist/core/validation/validators/max-length.d.ts +3 -10
- package/dist/core/validation/validators/max.d.ts +3 -10
- package/dist/core/validation/validators/min-age.d.ts +36 -0
- package/dist/core/validation/validators/min-date.d.ts +36 -0
- package/dist/core/validation/validators/min-length.d.ts +3 -10
- package/dist/core/validation/validators/min.d.ts +3 -10
- package/dist/core/validation/validators/number.d.ts +2 -9
- package/dist/core/validation/validators/past-date.d.ts +35 -0
- package/dist/core/validation/validators/pattern.d.ts +2 -9
- package/dist/core/validation/validators/phone.d.ts +2 -9
- package/dist/core/validation/validators/required.d.ts +2 -9
- package/dist/core/validation/validators/url.d.ts +2 -9
- package/dist/date-utils-xUWFslTj.js +29 -0
- package/dist/field-path-DuKdGcIE.js +66 -0
- package/dist/hooks/types.d.ts +328 -0
- package/dist/hooks/useArrayLength.d.ts +31 -0
- package/dist/hooks/useFormControl.d.ts +15 -39
- package/dist/hooks/useFormControlValue.d.ts +167 -0
- package/dist/hooks/useHiddenCondition.d.ts +25 -0
- package/dist/hooks/useSignalSubscription.d.ts +17 -0
- package/dist/index-D25LsbRm.js +73 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1248 -1357
- package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
- package/dist/safe-effect-Dh8uw81c.js +20 -0
- package/dist/validate-C3XiA_zf.js +10 -0
- package/dist/validators/email.d.ts +2 -0
- package/dist/validators/email.js +13 -0
- package/dist/validators/future-date.d.ts +2 -0
- package/dist/validators/future-date.js +20 -0
- package/dist/validators/is-date.d.ts +2 -0
- package/dist/validators/is-date.js +12 -0
- package/dist/validators/max-age.d.ts +2 -0
- package/dist/validators/max-age.js +20 -0
- package/dist/validators/max-date.d.ts +2 -0
- package/dist/validators/max-date.js +20 -0
- package/dist/validators/max-length.d.ts +2 -0
- package/dist/validators/max-length.js +11 -0
- package/dist/validators/max.d.ts +2 -0
- package/dist/validators/max.js +11 -0
- package/dist/validators/min-age.d.ts +2 -0
- package/dist/validators/min-age.js +20 -0
- package/dist/validators/min-date.d.ts +2 -0
- package/dist/validators/min-date.js +20 -0
- package/dist/validators/min-length.d.ts +2 -0
- package/dist/validators/min-length.js +11 -0
- package/dist/validators/min.d.ts +2 -0
- package/dist/validators/min.js +11 -0
- package/dist/validators/number.d.ts +2 -0
- package/dist/validators/number.js +35 -0
- package/dist/validators/past-date.d.ts +2 -0
- package/dist/validators/past-date.js +20 -0
- package/dist/validators/pattern.d.ts +2 -0
- package/dist/validators/pattern.js +11 -0
- package/dist/validators/phone.d.ts +2 -0
- package/dist/validators/phone.js +35 -0
- package/dist/validators/required.d.ts +2 -0
- package/dist/validators/required.js +15 -0
- package/dist/validators/url.d.ts +2 -0
- package/dist/validators/url.js +19 -0
- package/dist/validators-BGsNOgT1.js +207 -0
- package/dist/validators.js +53 -28
- package/llms.txt +8069 -357
- package/package.json +87 -8
- package/dist/behaviors-BRaiR-UY.js +0 -528
- package/dist/core/behavior/behavior-applicator.d.ts +0 -71
- package/dist/core/behavior/create-field-path.d.ts +0 -20
- package/dist/core/context/form-context-impl.d.ts +0 -29
- package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
- package/dist/core/nodes/group-node/index.d.ts +0 -11
- package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
- package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
- package/dist/core/utils/debounce.d.ts +0 -160
- package/dist/core/utils/resources.d.ts +0 -41
- package/dist/core/validation/validators/date.d.ts +0 -38
- package/dist/registry-helpers-BfCZcMkO.js +0 -426
- package/dist/validators-DjXtDVoE.js +0 -455
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { useRef as
|
|
9
|
-
|
|
1
|
+
import { signal as g, computed as d, effect as m, batch as j } from "@preact/signals-core";
|
|
2
|
+
import { F as S, E as V, V as K, B as H } from "./registry-helpers-Bv_BJ1s-.js";
|
|
3
|
+
import { A as ke, R as xe, b as Fe, g as Te } from "./registry-helpers-Bv_BJ1s-.js";
|
|
4
|
+
import { i as M, a as N, A as z, V as J, T as Y } from "./validators-BGsNOgT1.js";
|
|
5
|
+
import { g as Pe, c as Ce, d as De, v as Re, b as Me } from "./validators-BGsNOgT1.js";
|
|
6
|
+
import { c as I } from "./field-path-DuKdGcIE.js";
|
|
7
|
+
import { a as Ie, e as Le, t as Be } from "./field-path-DuKdGcIE.js";
|
|
8
|
+
import U, { useRef as P, useCallback as b, useSyncExternalStore as Q } from "react";
|
|
9
|
+
import { i as $e } from "./index-D25LsbRm.js";
|
|
10
|
+
import { r as We, s as qe, a as je } from "./safe-effect-Dh8uw81c.js";
|
|
11
|
+
class C {
|
|
10
12
|
// ============================================================================
|
|
11
13
|
// Protected состояние (для Template Method паттерна)
|
|
12
14
|
// ============================================================================
|
|
@@ -14,17 +16,17 @@ class A {
|
|
|
14
16
|
* Пользователь взаимодействовал с узлом (touched)
|
|
15
17
|
* Protected: наследники могут читать/изменять через методы
|
|
16
18
|
*/
|
|
17
|
-
_touched =
|
|
19
|
+
_touched = g(!1);
|
|
18
20
|
/**
|
|
19
21
|
* Значение узла было изменено (dirty)
|
|
20
22
|
* Protected: наследники могут читать/изменять через методы
|
|
21
23
|
*/
|
|
22
|
-
_dirty =
|
|
24
|
+
_dirty = g(!1);
|
|
23
25
|
/**
|
|
24
26
|
* Текущий статус узла
|
|
25
27
|
* Protected: наследники могут читать/изменять через методы
|
|
26
28
|
*/
|
|
27
|
-
_status =
|
|
29
|
+
_status = g("valid");
|
|
28
30
|
// ============================================================================
|
|
29
31
|
// Публичные computed signals (readonly для внешнего мира)
|
|
30
32
|
// ============================================================================
|
|
@@ -32,33 +34,33 @@ class A {
|
|
|
32
34
|
* Пользователь взаимодействовал с узлом (touched)
|
|
33
35
|
* Computed из _touched для предоставления readonly интерфейса
|
|
34
36
|
*/
|
|
35
|
-
touched =
|
|
37
|
+
touched = d(() => this._touched.value);
|
|
36
38
|
/**
|
|
37
39
|
* Пользователь не взаимодействовал с узлом (untouched)
|
|
38
40
|
*/
|
|
39
|
-
untouched =
|
|
41
|
+
untouched = d(() => !this._touched.value);
|
|
40
42
|
/**
|
|
41
43
|
* Значение узла было изменено (dirty)
|
|
42
44
|
* Computed из _dirty для предоставления readonly интерфейса
|
|
43
45
|
*/
|
|
44
|
-
dirty =
|
|
46
|
+
dirty = d(() => this._dirty.value);
|
|
45
47
|
/**
|
|
46
48
|
* Значение узла не было изменено (pristine)
|
|
47
49
|
*/
|
|
48
|
-
pristine =
|
|
50
|
+
pristine = d(() => !this._dirty.value);
|
|
49
51
|
/**
|
|
50
52
|
* Текущий статус узла
|
|
51
53
|
* Computed из _status для предоставления readonly интерфейса
|
|
52
54
|
*/
|
|
53
|
-
status =
|
|
55
|
+
status = d(() => this._status.value);
|
|
54
56
|
/**
|
|
55
57
|
* Узел отключен (disabled)
|
|
56
58
|
*/
|
|
57
|
-
disabled =
|
|
59
|
+
disabled = d(() => this._status.value === "disabled");
|
|
58
60
|
/**
|
|
59
61
|
* Узел включен (enabled)
|
|
60
62
|
*/
|
|
61
|
-
enabled =
|
|
63
|
+
enabled = d(() => this._status.value !== "disabled");
|
|
62
64
|
/**
|
|
63
65
|
* Получить ошибки валидации с фильтрацией
|
|
64
66
|
*
|
|
@@ -100,17 +102,17 @@ class A {
|
|
|
100
102
|
*/
|
|
101
103
|
getErrors(e) {
|
|
102
104
|
const t = this.errors.value;
|
|
103
|
-
return e ? t.filter((
|
|
104
|
-
if (e.code !== void 0 && !(Array.isArray(e.code) ? e.code : [e.code]).includes(
|
|
105
|
+
return e ? t.filter((s) => {
|
|
106
|
+
if (e.code !== void 0 && !(Array.isArray(e.code) ? e.code : [e.code]).includes(s.code) || e.message !== void 0 && !s.message.toLowerCase().includes(e.message.toLowerCase()))
|
|
105
107
|
return !1;
|
|
106
108
|
if (e.params !== void 0) {
|
|
107
|
-
if (!
|
|
109
|
+
if (!s.params)
|
|
108
110
|
return !1;
|
|
109
|
-
for (const [
|
|
110
|
-
if (
|
|
111
|
+
for (const [i, r] of Object.entries(e.params))
|
|
112
|
+
if (s.params[i] !== r)
|
|
111
113
|
return !1;
|
|
112
114
|
}
|
|
113
|
-
return !(e.predicate !== void 0 && !e.predicate(
|
|
115
|
+
return !(e.predicate !== void 0 && !e.predicate(s));
|
|
114
116
|
}) : t;
|
|
115
117
|
}
|
|
116
118
|
// ============================================================================
|
|
@@ -283,7 +285,7 @@ class A {
|
|
|
283
285
|
onEnable() {
|
|
284
286
|
}
|
|
285
287
|
}
|
|
286
|
-
class
|
|
288
|
+
class D {
|
|
287
289
|
/**
|
|
288
290
|
* Хранилище подписок
|
|
289
291
|
* Ключ: уникальный идентификатор подписки
|
|
@@ -451,157 +453,196 @@ class _ {
|
|
|
451
453
|
this.clear();
|
|
452
454
|
}
|
|
453
455
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
+
const w = {
|
|
457
|
+
/** FieldNode.watch() */
|
|
458
|
+
Watch: "watch",
|
|
459
|
+
/** FieldNode.computeFrom() */
|
|
460
|
+
ComputeFrom: "computeFrom",
|
|
461
|
+
/** GroupNode.linkFields() */
|
|
462
|
+
LinkFields: "linkFields",
|
|
463
|
+
/** GroupNode.watchField() */
|
|
464
|
+
WatchField: "watchField",
|
|
465
|
+
/** ArrayNode.watchItems() */
|
|
466
|
+
WatchItems: "watchItems",
|
|
467
|
+
/** ArrayNode.watchLength() */
|
|
468
|
+
WatchLength: "watchLength"
|
|
469
|
+
};
|
|
470
|
+
let X = 0;
|
|
471
|
+
function A(a) {
|
|
472
|
+
return `${a}-${++X}`;
|
|
473
|
+
}
|
|
474
|
+
class Z {
|
|
475
|
+
/** Внутренний сигнал статуса */
|
|
476
|
+
_status;
|
|
477
|
+
/** Публичный read-only сигнал статуса */
|
|
478
|
+
status;
|
|
479
|
+
/** Поле валидно */
|
|
480
|
+
valid;
|
|
481
|
+
/** Поле невалидно */
|
|
482
|
+
invalid;
|
|
483
|
+
/** Идет валидация */
|
|
484
|
+
pending;
|
|
485
|
+
/** Поле отключено */
|
|
486
|
+
disabled;
|
|
456
487
|
/**
|
|
457
|
-
*
|
|
488
|
+
* @param initial - Начальный статус (по умолчанию 'valid')
|
|
489
|
+
*/
|
|
490
|
+
constructor(e = "valid") {
|
|
491
|
+
this._status = g(e), this.status = d(() => this._status.value), this.valid = d(() => this._status.value === "valid"), this.invalid = d(() => this._status.value === "invalid"), this.pending = d(() => this._status.value === "pending"), this.disabled = d(() => this._status.value === "disabled");
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Начать валидацию
|
|
458
495
|
*
|
|
459
|
-
*
|
|
460
|
-
* @param context Контекст ошибки для логирования (например, 'AsyncValidator', 'BehaviorRegistry')
|
|
461
|
-
* @param strategy Стратегия обработки (THROW | LOG | CONVERT)
|
|
462
|
-
* @returns ValidationError если strategy = CONVERT, undefined если strategy = LOG, никогда не возвращается если strategy = THROW
|
|
496
|
+
* Переводит статус в 'pending' если поле не отключено
|
|
463
497
|
*
|
|
464
498
|
* @example
|
|
465
499
|
* ```typescript
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
500
|
+
* statusMachine.startValidation();
|
|
501
|
+
* // status: 'pending'
|
|
502
|
+
* ```
|
|
503
|
+
*/
|
|
504
|
+
startValidation() {
|
|
505
|
+
this._status.value !== "disabled" && (this._status.value = "pending");
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Завершить валидацию
|
|
473
509
|
*
|
|
474
|
-
*
|
|
475
|
-
* try {
|
|
476
|
-
* nonCriticalOperation();
|
|
477
|
-
* } catch (error) {
|
|
478
|
-
* FormErrorHandler.handle(error, 'NonCritical', ErrorStrategy.LOG);
|
|
479
|
-
* // Продолжаем выполнение
|
|
480
|
-
* }
|
|
510
|
+
* @param hasErrors - Есть ли ошибки валидации
|
|
481
511
|
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
* return validationError;
|
|
492
|
-
* }
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* // Валидация успешна
|
|
515
|
+
* statusMachine.completeValidation(false);
|
|
516
|
+
* // status: 'valid'
|
|
517
|
+
*
|
|
518
|
+
* // Есть ошибки
|
|
519
|
+
* statusMachine.completeValidation(true);
|
|
520
|
+
* // status: 'invalid'
|
|
493
521
|
* ```
|
|
494
522
|
*/
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
switch (i) {
|
|
498
|
-
case "throw":
|
|
499
|
-
throw e;
|
|
500
|
-
case "log":
|
|
501
|
-
return;
|
|
502
|
-
case "convert":
|
|
503
|
-
return {
|
|
504
|
-
code: "validator_error",
|
|
505
|
-
message: s,
|
|
506
|
-
params: { field: t }
|
|
507
|
-
};
|
|
508
|
-
}
|
|
523
|
+
completeValidation(e) {
|
|
524
|
+
(this._status.value === "pending" || this._status.value !== "disabled") && (this._status.value = e ? "invalid" : "valid");
|
|
509
525
|
}
|
|
510
526
|
/**
|
|
511
|
-
*
|
|
527
|
+
* Установить ошибки напрямую (без перехода через pending)
|
|
512
528
|
*
|
|
513
|
-
*
|
|
514
|
-
* - Error объекты → error.message
|
|
515
|
-
* - Строки → возвращает как есть
|
|
516
|
-
* - Объекты с message → извлекает message
|
|
517
|
-
* - Другое → String(error)
|
|
529
|
+
* Используется для синхронной валидации или установки ошибок извне
|
|
518
530
|
*
|
|
519
|
-
* @param
|
|
520
|
-
* @returns Сообщение ошибки
|
|
521
|
-
* @private
|
|
531
|
+
* @param hasErrors - Есть ли ошибки
|
|
522
532
|
*
|
|
523
533
|
* @example
|
|
524
534
|
* ```typescript
|
|
525
|
-
*
|
|
526
|
-
* // '
|
|
527
|
-
*
|
|
528
|
-
|
|
529
|
-
|
|
535
|
+
* statusMachine.setErrors(true);
|
|
536
|
+
* // status: 'invalid'
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
setErrors(e) {
|
|
540
|
+
this._status.value !== "disabled" && (this._status.value = e ? "invalid" : "valid");
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Отключить поле
|
|
530
544
|
*
|
|
531
|
-
*
|
|
532
|
-
* // 'Object error'
|
|
545
|
+
* Переводит статус в 'disabled'
|
|
533
546
|
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```typescript
|
|
549
|
+
* statusMachine.disable();
|
|
550
|
+
* // status: 'disabled'
|
|
536
551
|
* ```
|
|
537
552
|
*/
|
|
538
|
-
|
|
539
|
-
|
|
553
|
+
disable() {
|
|
554
|
+
this._status.value = "disabled";
|
|
540
555
|
}
|
|
541
556
|
/**
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
* Утилитная функция для создания ValidationError объектов
|
|
557
|
+
* Включить поле
|
|
545
558
|
*
|
|
546
|
-
* @param
|
|
547
|
-
* @param message Сообщение ошибки
|
|
548
|
-
* @param field Поле (опционально)
|
|
549
|
-
* @returns ValidationError объект
|
|
559
|
+
* @param hasErrors - Есть ли ошибки (определяет valid/invalid)
|
|
550
560
|
*
|
|
551
561
|
* @example
|
|
552
562
|
* ```typescript
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
* // { code: 'required', message: 'This field is required', field: 'email' }
|
|
563
|
+
* statusMachine.enable(false);
|
|
564
|
+
* // status: 'valid'
|
|
565
|
+
*
|
|
566
|
+
* statusMachine.enable(true);
|
|
567
|
+
* // status: 'invalid'
|
|
559
568
|
* ```
|
|
560
569
|
*/
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
code: e,
|
|
564
|
-
message: t,
|
|
565
|
-
params: i ? { field: i } : void 0
|
|
566
|
-
};
|
|
570
|
+
enable(e = !1) {
|
|
571
|
+
this._status.value = e ? "invalid" : "valid";
|
|
567
572
|
}
|
|
568
573
|
/**
|
|
569
|
-
*
|
|
574
|
+
* Обработать событие (альтернативный API)
|
|
570
575
|
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* @param value Значение для проверки
|
|
574
|
-
* @returns true если value является ValidationError
|
|
576
|
+
* @param event - Событие для обработки
|
|
575
577
|
*
|
|
576
578
|
* @example
|
|
577
579
|
* ```typescript
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
* }
|
|
580
|
+
* statusMachine.dispatch({ type: 'START_VALIDATION' });
|
|
581
|
+
* statusMachine.dispatch({ type: 'VALIDATION_FAILURE' });
|
|
581
582
|
* ```
|
|
582
583
|
*/
|
|
583
|
-
|
|
584
|
-
|
|
584
|
+
dispatch(e) {
|
|
585
|
+
switch (e.type) {
|
|
586
|
+
case "START_VALIDATION":
|
|
587
|
+
this.startValidation();
|
|
588
|
+
break;
|
|
589
|
+
case "VALIDATION_SUCCESS":
|
|
590
|
+
this.completeValidation(!1);
|
|
591
|
+
break;
|
|
592
|
+
case "VALIDATION_FAILURE":
|
|
593
|
+
this.completeValidation(!0);
|
|
594
|
+
break;
|
|
595
|
+
case "DISABLE":
|
|
596
|
+
this.disable();
|
|
597
|
+
break;
|
|
598
|
+
case "ENABLE":
|
|
599
|
+
this.enable(e.hasErrors);
|
|
600
|
+
break;
|
|
601
|
+
case "SET_ERRORS":
|
|
602
|
+
this.setErrors(e.hasErrors);
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Получить текущий статус
|
|
608
|
+
*/
|
|
609
|
+
getStatus() {
|
|
610
|
+
return this._status.value;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Проверить, можно ли начать валидацию
|
|
614
|
+
*/
|
|
615
|
+
canValidate() {
|
|
616
|
+
return this._status.value !== "disabled";
|
|
585
617
|
}
|
|
586
618
|
}
|
|
587
|
-
class
|
|
619
|
+
class ee extends C {
|
|
588
620
|
// ============================================================================
|
|
589
621
|
// Приватные сигналы
|
|
590
622
|
// ============================================================================
|
|
591
623
|
_value;
|
|
592
624
|
_errors;
|
|
593
|
-
// _touched, _dirty
|
|
594
|
-
|
|
625
|
+
// _touched, _dirty наследуются от FormNode (protected)
|
|
626
|
+
// _status управляется через statusMachine
|
|
595
627
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
596
628
|
_componentProps;
|
|
629
|
+
/**
|
|
630
|
+
* State machine для управления статусом поля
|
|
631
|
+
* Централизует логику переходов между valid/invalid/pending/disabled
|
|
632
|
+
*/
|
|
633
|
+
statusMachine;
|
|
597
634
|
// ============================================================================
|
|
598
635
|
// Публичные computed signals
|
|
599
636
|
// ============================================================================
|
|
600
637
|
value;
|
|
638
|
+
// valid, invalid, pending, status, disabled берутся из statusMachine
|
|
601
639
|
valid;
|
|
602
640
|
invalid;
|
|
603
|
-
// touched, dirty, status наследуются от FormNode
|
|
604
641
|
pending;
|
|
642
|
+
// Override status и disabled из базового класса
|
|
643
|
+
status;
|
|
644
|
+
disabled;
|
|
645
|
+
// touched, dirty наследуются от FormNode
|
|
605
646
|
errors;
|
|
606
647
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
607
648
|
componentProps;
|
|
@@ -617,22 +658,26 @@ class I extends A {
|
|
|
617
658
|
asyncValidators;
|
|
618
659
|
updateOn;
|
|
619
660
|
initialValue;
|
|
620
|
-
|
|
661
|
+
currentAbortController;
|
|
621
662
|
debounceMs;
|
|
622
663
|
validateDebounceTimer;
|
|
623
|
-
|
|
664
|
+
/**
|
|
665
|
+
* Pending debounced validation state
|
|
666
|
+
* Contains resolve function and AbortController for cancellation
|
|
667
|
+
*/
|
|
668
|
+
pendingValidation;
|
|
624
669
|
/**
|
|
625
670
|
* Менеджер подписок для централизованного cleanup
|
|
626
671
|
* Использует SubscriptionManager вместо массива для управления подписками
|
|
627
672
|
*/
|
|
628
|
-
disposers = new
|
|
673
|
+
disposers = new D();
|
|
629
674
|
component;
|
|
630
675
|
// ============================================================================
|
|
631
676
|
// Конструктор
|
|
632
677
|
// ============================================================================
|
|
633
678
|
constructor(e) {
|
|
634
|
-
super(), this.initialValue = e.value, this.validators = e.validators || [], this.asyncValidators = e.asyncValidators || [], this.updateOn = e.updateOn || "blur", this.debounceMs = e.debounce || 0, this.component = e.component, this._value =
|
|
635
|
-
() => this.
|
|
679
|
+
super(), this.initialValue = e.value, this.validators = e.validators || [], this.asyncValidators = e.asyncValidators || [], this.updateOn = e.updateOn || "blur", this.debounceMs = e.debounce || 0, this.component = e.component, this._value = g(e.value), this._errors = g([]), this._componentProps = g(e.componentProps || {}), this.statusMachine = new Z(e.disabled ? "disabled" : "valid"), this.value = d(() => this._value.value), this.valid = d(() => this.statusMachine.valid.value), this.invalid = d(() => this.statusMachine.invalid.value), this.pending = d(() => this.statusMachine.pending.value), this.status = d(() => this.statusMachine.status.value), this.disabled = d(() => this.statusMachine.disabled.value), this.errors = d(() => this._errors.value), this.componentProps = d(() => this._componentProps.value), this.shouldShowError = d(
|
|
680
|
+
() => this.statusMachine.invalid.value && (this._touched.value || this._dirty.value)
|
|
636
681
|
);
|
|
637
682
|
}
|
|
638
683
|
// ============================================================================
|
|
@@ -644,12 +689,12 @@ class I extends A {
|
|
|
644
689
|
setValue(e, t) {
|
|
645
690
|
if (this._value.value = e, this._dirty.value = !0, t?.emitEvent === !1)
|
|
646
691
|
return;
|
|
647
|
-
const
|
|
692
|
+
const s = this.validators.length > 0 || this.asyncValidators.length > 0, i = this._errors.value.length > 0;
|
|
648
693
|
if (this.updateOn === "change") {
|
|
649
694
|
this.validate();
|
|
650
695
|
return;
|
|
651
696
|
}
|
|
652
|
-
|
|
697
|
+
i && s && this.validate();
|
|
653
698
|
}
|
|
654
699
|
patchValue(e) {
|
|
655
700
|
this.setValue(e);
|
|
@@ -678,7 +723,7 @@ class I extends A {
|
|
|
678
723
|
* ```
|
|
679
724
|
*/
|
|
680
725
|
reset(e) {
|
|
681
|
-
this._value.value = e !== void 0 ? e : this.initialValue, this._errors.value = [], this._touched.value = !1, this._dirty.value = !1, this.
|
|
726
|
+
this._value.value = e !== void 0 ? e : this.initialValue, this._errors.value = [], this._touched.value = !1, this._dirty.value = !1, this.statusMachine.setErrors(!1);
|
|
682
727
|
}
|
|
683
728
|
/**
|
|
684
729
|
* Сбросить поле к исходному значению (initialValue)
|
|
@@ -708,13 +753,25 @@ class I extends A {
|
|
|
708
753
|
resetToInitial() {
|
|
709
754
|
this.reset(this.initialValue);
|
|
710
755
|
}
|
|
756
|
+
/**
|
|
757
|
+
* Cancel any pending validation (debounced or running)
|
|
758
|
+
* @private
|
|
759
|
+
* @remarks
|
|
760
|
+
* Centralizes all cancellation logic:
|
|
761
|
+
* - Aborts pending debounced validation and resolves its promise
|
|
762
|
+
* - Clears debounce timer
|
|
763
|
+
* - Aborts currently running async validation
|
|
764
|
+
*/
|
|
765
|
+
cancelPendingValidation() {
|
|
766
|
+
this.pendingValidation && (this.pendingValidation.abortController.abort(), this.pendingValidation.resolve(!1), this.pendingValidation = void 0), this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0), this.currentAbortController && (this.currentAbortController.abort(), this.currentAbortController = void 0);
|
|
767
|
+
}
|
|
711
768
|
/**
|
|
712
769
|
* Запустить валидацию поля
|
|
713
770
|
* @param options - опции валидации
|
|
714
771
|
* @returns `Promise<boolean>` - true если поле валидно
|
|
715
772
|
*
|
|
716
773
|
* @remarks
|
|
717
|
-
* Метод защищен от race conditions через
|
|
774
|
+
* Метод защищен от race conditions через AbortController.
|
|
718
775
|
* При быстром вводе только последняя валидация применяет результаты.
|
|
719
776
|
*
|
|
720
777
|
* @example
|
|
@@ -728,69 +785,102 @@ class I extends A {
|
|
|
728
785
|
*/
|
|
729
786
|
async validate(e) {
|
|
730
787
|
const t = e?.debounce ?? this.debounceMs;
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
788
|
+
if (this.cancelPendingValidation(), t <= 0 || this.asyncValidators.length === 0)
|
|
789
|
+
return this.validateImmediate();
|
|
790
|
+
const s = new AbortController();
|
|
791
|
+
return new Promise((i) => {
|
|
792
|
+
this.pendingValidation = { resolve: i, abortController: s }, this.validateDebounceTimer = setTimeout(async () => {
|
|
793
|
+
if (this.validateDebounceTimer = void 0, s.signal.aborted) {
|
|
735
794
|
i(!1);
|
|
736
795
|
return;
|
|
737
796
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
797
|
+
this.pendingValidation = void 0;
|
|
798
|
+
const r = await this.validateImmediate(s);
|
|
799
|
+
i(r);
|
|
800
|
+
}, t), s.signal.addEventListener(
|
|
801
|
+
"abort",
|
|
802
|
+
() => {
|
|
803
|
+
this.validateDebounceTimer && (clearTimeout(this.validateDebounceTimer), this.validateDebounceTimer = void 0), i(!1);
|
|
804
|
+
},
|
|
805
|
+
{ once: !0 }
|
|
806
|
+
);
|
|
807
|
+
});
|
|
742
808
|
}
|
|
743
809
|
/**
|
|
744
810
|
* Немедленная валидация без debounce
|
|
745
811
|
* @private
|
|
812
|
+
* @param providedController - AbortController from debounced validate()
|
|
746
813
|
* @remarks
|
|
747
|
-
* Защищена от race conditions:
|
|
748
|
-
* -
|
|
749
|
-
* -
|
|
750
|
-
* -
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
const
|
|
756
|
-
for (const
|
|
757
|
-
const
|
|
758
|
-
|
|
814
|
+
* Защищена от race conditions через AbortController:
|
|
815
|
+
* - Отменяет предыдущую валидацию при запуске новой (if no controller provided)
|
|
816
|
+
* - Передаёт AbortSignal в async валидаторы для отмены операций (например, fetch)
|
|
817
|
+
* - Проверяет signal.aborted в ключевых точках
|
|
818
|
+
*/
|
|
819
|
+
async validateImmediate(e) {
|
|
820
|
+
const t = e ?? new AbortController();
|
|
821
|
+
e || this.currentAbortController?.abort(), this.currentAbortController = t;
|
|
822
|
+
const { signal: s } = t, i = [];
|
|
823
|
+
for (const n of this.validators) {
|
|
824
|
+
const o = n(this._value.value);
|
|
825
|
+
o && i.push(o);
|
|
759
826
|
}
|
|
760
|
-
if (
|
|
827
|
+
if (s.aborted)
|
|
761
828
|
return !1;
|
|
762
|
-
if (
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
if (
|
|
829
|
+
if (i.length > 0) {
|
|
830
|
+
this._errors.value = i;
|
|
831
|
+
const n = i.some((o) => o.severity !== "warning");
|
|
832
|
+
if (this.statusMachine.setErrors(n), n)
|
|
766
833
|
return !1;
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
try {
|
|
771
|
-
return await n(this._value.value);
|
|
772
|
-
} catch (o) {
|
|
773
|
-
return E.handle(
|
|
774
|
-
o,
|
|
775
|
-
"FieldNode AsyncValidator",
|
|
776
|
-
g.CONVERT
|
|
777
|
-
);
|
|
778
|
-
}
|
|
779
|
-
})
|
|
780
|
-
);
|
|
781
|
-
if (e !== this.currentValidationId || (this._pending.value = !1, e !== this.currentValidationId))
|
|
834
|
+
}
|
|
835
|
+
if (this.asyncValidators.length > 0) {
|
|
836
|
+
if (s.aborted)
|
|
782
837
|
return !1;
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
838
|
+
this.statusMachine.startValidation();
|
|
839
|
+
try {
|
|
840
|
+
const n = await Promise.all(
|
|
841
|
+
this.asyncValidators.map(async (l) => {
|
|
842
|
+
if (s.aborted)
|
|
843
|
+
throw new DOMException("Validation aborted", "AbortError");
|
|
844
|
+
try {
|
|
845
|
+
const u = await l(this._value.value, { signal: s });
|
|
846
|
+
if (s.aborted)
|
|
847
|
+
throw new DOMException("Validation aborted", "AbortError");
|
|
848
|
+
return u;
|
|
849
|
+
} catch (u) {
|
|
850
|
+
if (u instanceof DOMException && u.name === "AbortError")
|
|
851
|
+
throw u;
|
|
852
|
+
return S.handle(
|
|
853
|
+
u,
|
|
854
|
+
"FieldNode AsyncValidator",
|
|
855
|
+
V.CONVERT
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
})
|
|
859
|
+
);
|
|
860
|
+
if (s.aborted)
|
|
861
|
+
return !1;
|
|
862
|
+
const o = n.filter(Boolean);
|
|
863
|
+
if (o.length > 0) {
|
|
864
|
+
this._errors.value = o;
|
|
865
|
+
const l = o.some((u) => u.severity !== "warning");
|
|
866
|
+
if (this.statusMachine.completeValidation(l), l)
|
|
867
|
+
return !1;
|
|
868
|
+
}
|
|
869
|
+
} catch (n) {
|
|
870
|
+
if (n instanceof DOMException && n.name === "AbortError")
|
|
871
|
+
return !1;
|
|
872
|
+
throw n;
|
|
873
|
+
}
|
|
786
874
|
}
|
|
787
|
-
return
|
|
875
|
+
return s.aborted ? !1 : ((this.validators.length > 0 || this.asyncValidators.length > 0) && (this._errors.value = [], this.statusMachine.completeValidation(!1)), !this._errors.value.some((n) => n.severity !== "warning"));
|
|
788
876
|
}
|
|
789
877
|
setErrors(e) {
|
|
790
|
-
this._errors.value = e
|
|
878
|
+
this._errors.value = e;
|
|
879
|
+
const t = e.some((s) => s.severity !== "warning");
|
|
880
|
+
this.statusMachine.setErrors(t);
|
|
791
881
|
}
|
|
792
882
|
clearErrors() {
|
|
793
|
-
this._errors.value = [], this.
|
|
883
|
+
this._errors.value = [], this.statusMachine.setErrors(!1);
|
|
794
884
|
}
|
|
795
885
|
// ============================================================================
|
|
796
886
|
// Protected hooks (Template Method pattern)
|
|
@@ -806,18 +896,18 @@ class I extends A {
|
|
|
806
896
|
/**
|
|
807
897
|
* Hook: вызывается после disable()
|
|
808
898
|
*
|
|
809
|
-
* Для FieldNode: очищаем ошибки
|
|
899
|
+
* Для FieldNode: синхронизируем statusMachine и очищаем ошибки
|
|
810
900
|
*/
|
|
811
901
|
onDisable() {
|
|
812
|
-
this._errors.value = [];
|
|
902
|
+
this.statusMachine.disable(), this._errors.value = [];
|
|
813
903
|
}
|
|
814
904
|
/**
|
|
815
905
|
* Hook: вызывается после enable()
|
|
816
906
|
*
|
|
817
|
-
* Для FieldNode: запускаем валидацию
|
|
907
|
+
* Для FieldNode: синхронизируем statusMachine и запускаем валидацию
|
|
818
908
|
*/
|
|
819
909
|
onEnable() {
|
|
820
|
-
this.validate();
|
|
910
|
+
this.statusMachine.enable(this._errors.value.length > 0), this.validate();
|
|
821
911
|
}
|
|
822
912
|
/**
|
|
823
913
|
* Обновляет свойства компонента (например, опции для Select)
|
|
@@ -882,25 +972,35 @@ class I extends A {
|
|
|
882
972
|
* Подписка на изменения значения поля
|
|
883
973
|
* Автоматически отслеживает изменения через @preact/signals effect
|
|
884
974
|
*
|
|
885
|
-
* @param callback - Функция, вызываемая при изменении
|
|
975
|
+
* @param callback - Функция, вызываемая при изменении значения.
|
|
976
|
+
* Для async операций передается AbortSignal во втором параметре.
|
|
886
977
|
* @returns Функция отписки для cleanup
|
|
887
978
|
*
|
|
888
979
|
* @example
|
|
889
980
|
* ```typescript
|
|
981
|
+
* // Синхронный callback
|
|
890
982
|
* const unsubscribe = form.email.watch((value) => {
|
|
891
983
|
* console.log('Email changed:', value);
|
|
892
984
|
* });
|
|
893
985
|
*
|
|
986
|
+
* // Асинхронный callback с поддержкой отмены
|
|
987
|
+
* const unsubscribe = form.email.watch(async (value, signal) => {
|
|
988
|
+
* const result = await fetch('/api/validate', { signal });
|
|
989
|
+
* // ...
|
|
990
|
+
* });
|
|
991
|
+
*
|
|
894
992
|
* // Cleanup
|
|
895
993
|
* useEffect(() => unsubscribe, []);
|
|
896
994
|
* ```
|
|
897
995
|
*/
|
|
898
996
|
watch(e) {
|
|
899
|
-
const t =
|
|
900
|
-
const
|
|
901
|
-
e(
|
|
902
|
-
}), i =
|
|
903
|
-
return this.disposers.add(i,
|
|
997
|
+
const t = new AbortController(), s = m(() => {
|
|
998
|
+
const r = this.value.value;
|
|
999
|
+
e(r, t.signal);
|
|
1000
|
+
}), i = A(w.Watch);
|
|
1001
|
+
return this.disposers.add(i, () => {
|
|
1002
|
+
t.abort(), s();
|
|
1003
|
+
});
|
|
904
1004
|
}
|
|
905
1005
|
/**
|
|
906
1006
|
* Вычисляемое значение из других полей
|
|
@@ -925,16 +1025,23 @@ class I extends A {
|
|
|
925
1025
|
* ```
|
|
926
1026
|
*/
|
|
927
1027
|
computeFrom(e, t) {
|
|
928
|
-
const
|
|
929
|
-
const
|
|
1028
|
+
const s = m(() => {
|
|
1029
|
+
const r = e.map((o) => o.value), n = t(...r);
|
|
930
1030
|
this.setValue(n, { emitEvent: !1 });
|
|
931
|
-
}),
|
|
932
|
-
return this.disposers.add(
|
|
1031
|
+
}), i = A(w.ComputeFrom);
|
|
1032
|
+
return this.disposers.add(i, s);
|
|
933
1033
|
}
|
|
934
1034
|
/**
|
|
935
1035
|
* Очистить все ресурсы и таймеры
|
|
936
1036
|
* Должен вызываться при unmount компонента
|
|
937
1037
|
*
|
|
1038
|
+
* @remarks
|
|
1039
|
+
* Освобождает все ресурсы:
|
|
1040
|
+
* - Отписывает все subscriptions через SubscriptionManager
|
|
1041
|
+
* - Отменяет pending/running валидации через cancelPendingValidation()
|
|
1042
|
+
*
|
|
1043
|
+
* Использует try-finally для гарантированного cleanup даже при ошибках.
|
|
1044
|
+
*
|
|
938
1045
|
* @example
|
|
939
1046
|
* ```typescript
|
|
940
1047
|
* useEffect(() => {
|
|
@@ -945,10 +1052,14 @@ class I extends A {
|
|
|
945
1052
|
* ```
|
|
946
1053
|
*/
|
|
947
1054
|
dispose() {
|
|
948
|
-
|
|
1055
|
+
try {
|
|
1056
|
+
this.disposers.dispose();
|
|
1057
|
+
} finally {
|
|
1058
|
+
this.cancelPendingValidation();
|
|
1059
|
+
}
|
|
949
1060
|
}
|
|
950
1061
|
}
|
|
951
|
-
class
|
|
1062
|
+
class te {
|
|
952
1063
|
form;
|
|
953
1064
|
constructor(e) {
|
|
954
1065
|
this.form = e;
|
|
@@ -964,8 +1075,8 @@ class N {
|
|
|
964
1075
|
* @param validators Зарегистрированные валидаторы
|
|
965
1076
|
*/
|
|
966
1077
|
async apply(e) {
|
|
967
|
-
const { validatorsByField: t, treeValidators:
|
|
968
|
-
await this.applyFieldValidators(t), this.applyTreeValidators(
|
|
1078
|
+
const { validatorsByField: t, treeValidators: s } = this.groupValidators(e);
|
|
1079
|
+
await this.applyFieldValidators(t), this.applyTreeValidators(s);
|
|
969
1080
|
}
|
|
970
1081
|
/**
|
|
971
1082
|
* Группировка валидаторов по типам
|
|
@@ -978,15 +1089,15 @@ class N {
|
|
|
978
1089
|
* @returns Сгруппированные валидаторы
|
|
979
1090
|
*/
|
|
980
1091
|
groupValidators(e) {
|
|
981
|
-
const t = /* @__PURE__ */ new Map(),
|
|
982
|
-
for (const
|
|
983
|
-
if (
|
|
984
|
-
|
|
1092
|
+
const t = /* @__PURE__ */ new Map(), s = [];
|
|
1093
|
+
for (const i of e)
|
|
1094
|
+
if (i.type === "tree")
|
|
1095
|
+
s.push(i);
|
|
985
1096
|
else {
|
|
986
|
-
const
|
|
987
|
-
|
|
1097
|
+
const r = t.get(i.fieldPath) || [];
|
|
1098
|
+
r.push(i), t.set(i.fieldPath, r);
|
|
988
1099
|
}
|
|
989
|
-
return { validatorsByField: t, treeValidators:
|
|
1100
|
+
return { validatorsByField: t, treeValidators: s };
|
|
990
1101
|
}
|
|
991
1102
|
/**
|
|
992
1103
|
* Применение field валидаторов к полям
|
|
@@ -1000,31 +1111,44 @@ class N {
|
|
|
1000
1111
|
* @param validatorsByField Валидаторы, сгруппированные по полям
|
|
1001
1112
|
*/
|
|
1002
1113
|
async applyFieldValidators(e) {
|
|
1003
|
-
for (const [t,
|
|
1004
|
-
const
|
|
1005
|
-
if (!
|
|
1114
|
+
for (const [t, s] of e) {
|
|
1115
|
+
const i = this.form.getFieldByPath(t);
|
|
1116
|
+
if (!i) {
|
|
1006
1117
|
console.warn(`Field ${t} not found in GroupNode`);
|
|
1007
1118
|
continue;
|
|
1008
1119
|
}
|
|
1009
|
-
if (!
|
|
1010
|
-
process.env.NODE_ENV !== "production" && console.warn(`Validation can only run on FieldNode, skipping ${t}`);
|
|
1120
|
+
if (!M(i) && !N(i)) {
|
|
1121
|
+
process.env.NODE_ENV !== "production" && console.warn(`Validation can only run on FieldNode or ArrayNode, skipping ${t}`);
|
|
1011
1122
|
continue;
|
|
1012
1123
|
}
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1124
|
+
const r = [];
|
|
1125
|
+
let n;
|
|
1126
|
+
if (N(i)) {
|
|
1127
|
+
const o = i.getValue();
|
|
1128
|
+
n = new z(this.form, t, o);
|
|
1129
|
+
} else
|
|
1130
|
+
n = new J(this.form, t, i);
|
|
1131
|
+
for (const o of s)
|
|
1015
1132
|
if (!(o.condition && !this.checkCondition(o.condition)))
|
|
1016
1133
|
try {
|
|
1017
|
-
let
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1134
|
+
let l = null;
|
|
1135
|
+
const u = n.value();
|
|
1136
|
+
if (o.type === "sync") {
|
|
1137
|
+
const v = o.validator;
|
|
1138
|
+
l = v(u, n);
|
|
1139
|
+
} else if (o.type === "async") {
|
|
1140
|
+
const v = o.validator;
|
|
1141
|
+
l = await v(u, n);
|
|
1142
|
+
}
|
|
1143
|
+
l && r.push(l);
|
|
1144
|
+
} catch (l) {
|
|
1145
|
+
S.handle(
|
|
1146
|
+
l,
|
|
1023
1147
|
`ValidationApplicator: validator for ${t}`,
|
|
1024
|
-
|
|
1148
|
+
V.LOG
|
|
1025
1149
|
);
|
|
1026
1150
|
}
|
|
1027
|
-
|
|
1151
|
+
r.length > 0 ? i.setErrors(r) : i.clearErrors();
|
|
1028
1152
|
}
|
|
1029
1153
|
}
|
|
1030
1154
|
/**
|
|
@@ -1037,24 +1161,24 @@ class N {
|
|
|
1037
1161
|
*/
|
|
1038
1162
|
applyTreeValidators(e) {
|
|
1039
1163
|
for (const t of e) {
|
|
1040
|
-
const
|
|
1164
|
+
const s = new Y(this.form);
|
|
1041
1165
|
if (!(t.condition && !this.checkCondition(t.condition)))
|
|
1042
1166
|
try {
|
|
1043
1167
|
if (t.type !== "tree")
|
|
1044
1168
|
continue;
|
|
1045
|
-
const
|
|
1046
|
-
if (
|
|
1047
|
-
const
|
|
1048
|
-
if (
|
|
1049
|
-
const
|
|
1050
|
-
if (
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1169
|
+
const i = t.validator, r = i(s);
|
|
1170
|
+
if (r && t.options && "targetField" in t.options) {
|
|
1171
|
+
const n = t.options.targetField;
|
|
1172
|
+
if (n) {
|
|
1173
|
+
const o = this.form.getFieldByPath(String(n));
|
|
1174
|
+
if (o && M(o)) {
|
|
1175
|
+
const l = o.errors.value;
|
|
1176
|
+
o.setErrors([...l, r]);
|
|
1053
1177
|
}
|
|
1054
1178
|
}
|
|
1055
1179
|
}
|
|
1056
|
-
} catch (
|
|
1057
|
-
|
|
1180
|
+
} catch (i) {
|
|
1181
|
+
S.handle(i, "ValidationApplicator: tree validator", V.LOG);
|
|
1058
1182
|
}
|
|
1059
1183
|
}
|
|
1060
1184
|
}
|
|
@@ -1071,61 +1195,11 @@ class N {
|
|
|
1071
1195
|
const t = this.form.getFieldByPath(e.fieldPath);
|
|
1072
1196
|
if (!t)
|
|
1073
1197
|
return !1;
|
|
1074
|
-
const
|
|
1075
|
-
return e.conditionFn(
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
class B {
|
|
1079
|
-
form;
|
|
1080
|
-
behaviorRegistry;
|
|
1081
|
-
constructor(e, t) {
|
|
1082
|
-
this.form = e, this.behaviorRegistry = t;
|
|
1083
|
-
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Применить behavior схему к форме
|
|
1086
|
-
*
|
|
1087
|
-
* Этапы:
|
|
1088
|
-
* 1. Начать регистрацию (beginRegistration)
|
|
1089
|
-
* 2. Выполнить схему (регистрация behaviors)
|
|
1090
|
-
* 3. Завершить регистрацию (endRegistration) - применить behaviors
|
|
1091
|
-
* 4. Вернуть функцию cleanup для отписки
|
|
1092
|
-
*
|
|
1093
|
-
* @param schemaFn Функция-схема behavior
|
|
1094
|
-
* @returns Функция отписки от всех behaviors
|
|
1095
|
-
*
|
|
1096
|
-
* @example
|
|
1097
|
-
* ```typescript
|
|
1098
|
-
* const cleanup = behaviorApplicator.apply((path) => {
|
|
1099
|
-
* copyFrom(path.residenceAddress, path.registrationAddress, {
|
|
1100
|
-
* when: (form) => form.sameAsRegistration === true
|
|
1101
|
-
* });
|
|
1102
|
-
*
|
|
1103
|
-
* enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
|
|
1104
|
-
*
|
|
1105
|
-
* computeFrom(
|
|
1106
|
-
* path.initialPayment,
|
|
1107
|
-
* [path.propertyValue],
|
|
1108
|
-
* (propertyValue) => propertyValue ? propertyValue * 0.2 : null
|
|
1109
|
-
* );
|
|
1110
|
-
* });
|
|
1111
|
-
*
|
|
1112
|
-
* // Cleanup при unmount
|
|
1113
|
-
* useEffect(() => cleanup, []);
|
|
1114
|
-
* ```
|
|
1115
|
-
*/
|
|
1116
|
-
apply(e) {
|
|
1117
|
-
this.behaviorRegistry.beginRegistration();
|
|
1118
|
-
try {
|
|
1119
|
-
const t = S();
|
|
1120
|
-
e(t);
|
|
1121
|
-
const i = this.form.getProxy();
|
|
1122
|
-
return this.behaviorRegistry.endRegistration(i).cleanup;
|
|
1123
|
-
} catch (t) {
|
|
1124
|
-
throw E.handle(t, "BehaviorApplicator", g.THROW), t;
|
|
1125
|
-
}
|
|
1198
|
+
const s = t.value.value;
|
|
1199
|
+
return e.conditionFn(s);
|
|
1126
1200
|
}
|
|
1127
1201
|
}
|
|
1128
|
-
class
|
|
1202
|
+
class se {
|
|
1129
1203
|
/**
|
|
1130
1204
|
* Парсит путь в массив сегментов
|
|
1131
1205
|
*
|
|
@@ -1152,22 +1226,22 @@ class O {
|
|
|
1152
1226
|
*/
|
|
1153
1227
|
parsePath(e) {
|
|
1154
1228
|
const t = [];
|
|
1155
|
-
let
|
|
1156
|
-
for (let
|
|
1157
|
-
const n = e[
|
|
1158
|
-
n === "[" ? (
|
|
1229
|
+
let s = "", i = !1;
|
|
1230
|
+
for (let r = 0; r < e.length; r++) {
|
|
1231
|
+
const n = e[r];
|
|
1232
|
+
n === "[" ? (i = !0, s += n) : n === "]" ? (i = !1, s += n) : n === "." && !i ? s && (this.addSegment(t, s), s = "") : s += n;
|
|
1159
1233
|
}
|
|
1160
|
-
return
|
|
1234
|
+
return s && this.addSegment(t, s), t;
|
|
1161
1235
|
}
|
|
1162
1236
|
/**
|
|
1163
1237
|
* Добавляет сегмент в массив, обрабатывая массивы
|
|
1164
1238
|
* @private
|
|
1165
1239
|
*/
|
|
1166
1240
|
addSegment(e, t) {
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1169
|
-
key:
|
|
1170
|
-
index: parseInt(
|
|
1241
|
+
const s = t.match(/^(.+)\[(\d+)\]$/);
|
|
1242
|
+
s ? e.push({
|
|
1243
|
+
key: s[1],
|
|
1244
|
+
index: parseInt(s[2], 10)
|
|
1171
1245
|
}) : e.push({ key: t });
|
|
1172
1246
|
}
|
|
1173
1247
|
/**
|
|
@@ -1202,16 +1276,16 @@ class O {
|
|
|
1202
1276
|
* ```
|
|
1203
1277
|
*/
|
|
1204
1278
|
getValueByPath(e, t) {
|
|
1205
|
-
const
|
|
1206
|
-
let
|
|
1207
|
-
for (const
|
|
1208
|
-
if (
|
|
1209
|
-
if (
|
|
1210
|
-
if (!Array.isArray(
|
|
1211
|
-
|
|
1279
|
+
const s = this.parsePath(t);
|
|
1280
|
+
let i = e;
|
|
1281
|
+
for (const r of s) {
|
|
1282
|
+
if (i == null) return;
|
|
1283
|
+
if (i = i[r.key], r.index !== void 0) {
|
|
1284
|
+
if (!Array.isArray(i)) return;
|
|
1285
|
+
i = i[r.index];
|
|
1212
1286
|
}
|
|
1213
1287
|
}
|
|
1214
|
-
return
|
|
1288
|
+
return i;
|
|
1215
1289
|
}
|
|
1216
1290
|
/**
|
|
1217
1291
|
* Устанавливает значение по пути в объекте (мутирует объект)
|
|
@@ -1240,31 +1314,31 @@ class O {
|
|
|
1240
1314
|
* // obj3.items[0].title === 'New'
|
|
1241
1315
|
* ```
|
|
1242
1316
|
*/
|
|
1243
|
-
setValueByPath(e, t,
|
|
1244
|
-
const
|
|
1245
|
-
if (
|
|
1317
|
+
setValueByPath(e, t, s) {
|
|
1318
|
+
const i = this.parsePath(t);
|
|
1319
|
+
if (i.length === 0)
|
|
1246
1320
|
throw new Error("Cannot set value: empty path");
|
|
1247
|
-
let
|
|
1248
|
-
for (let o = 0; o <
|
|
1249
|
-
const
|
|
1250
|
-
let
|
|
1251
|
-
if (
|
|
1252
|
-
if (!Array.isArray(
|
|
1253
|
-
throw new Error(`Expected array at path segment: ${
|
|
1254
|
-
|
|
1321
|
+
let r = e;
|
|
1322
|
+
for (let o = 0; o < i.length - 1; o++) {
|
|
1323
|
+
const l = i[o];
|
|
1324
|
+
let u = r[l.key];
|
|
1325
|
+
if (l.index !== void 0) {
|
|
1326
|
+
if (!Array.isArray(u))
|
|
1327
|
+
throw new Error(`Expected array at path segment: ${l.key}, but got ${typeof u}`);
|
|
1328
|
+
r = u[l.index];
|
|
1255
1329
|
} else
|
|
1256
|
-
|
|
1330
|
+
u == null && (r[l.key] = {}, u = r[l.key]), r = u;
|
|
1257
1331
|
}
|
|
1258
|
-
const n =
|
|
1332
|
+
const n = i[i.length - 1];
|
|
1259
1333
|
if (n.index !== void 0) {
|
|
1260
|
-
const o =
|
|
1334
|
+
const o = r[n.key];
|
|
1261
1335
|
if (!Array.isArray(o))
|
|
1262
1336
|
throw new Error(
|
|
1263
1337
|
`Expected array at path segment: ${n.key}, but got ${typeof o}`
|
|
1264
1338
|
);
|
|
1265
|
-
o[n.index] =
|
|
1339
|
+
o[n.index] = s;
|
|
1266
1340
|
} else
|
|
1267
|
-
|
|
1341
|
+
r[n.key] = s;
|
|
1268
1342
|
}
|
|
1269
1343
|
/**
|
|
1270
1344
|
* Получить значение из FormNode по пути
|
|
@@ -1301,9 +1375,9 @@ class O {
|
|
|
1301
1375
|
* ```
|
|
1302
1376
|
*/
|
|
1303
1377
|
getFormNodeValue(e, t) {
|
|
1304
|
-
const
|
|
1305
|
-
if (
|
|
1306
|
-
return this.isFormNode(
|
|
1378
|
+
const s = this.getNodeByPath(e, t);
|
|
1379
|
+
if (s != null)
|
|
1380
|
+
return this.isFormNode(s) ? s.value.value : s;
|
|
1307
1381
|
}
|
|
1308
1382
|
/**
|
|
1309
1383
|
* Type guard для проверки, является ли объект FormNode
|
|
@@ -1361,560 +1435,224 @@ class O {
|
|
|
1361
1435
|
* ```
|
|
1362
1436
|
*/
|
|
1363
1437
|
getNodeByPath(e, t) {
|
|
1364
|
-
const
|
|
1365
|
-
let
|
|
1366
|
-
for (const
|
|
1367
|
-
if (
|
|
1368
|
-
const n =
|
|
1438
|
+
const s = this.parsePath(t);
|
|
1439
|
+
let i = e;
|
|
1440
|
+
for (const r of s) {
|
|
1441
|
+
if (i == null) return null;
|
|
1442
|
+
const n = i;
|
|
1369
1443
|
if (n.fields && n.fields instanceof Map) {
|
|
1370
|
-
if (
|
|
1371
|
-
if (
|
|
1444
|
+
if (i = n.fields.get(r.key), r.index === void 0) {
|
|
1445
|
+
if (i == null) return null;
|
|
1372
1446
|
continue;
|
|
1373
1447
|
}
|
|
1374
|
-
} else if (
|
|
1448
|
+
} else if (r.index !== void 0 && n.items) {
|
|
1375
1449
|
const o = n.items.value || n.items;
|
|
1376
|
-
if (!Array.isArray(o) || (
|
|
1450
|
+
if (!Array.isArray(o) || (i = o[r.index], i == null)) return null;
|
|
1377
1451
|
continue;
|
|
1378
|
-
} else if (
|
|
1379
|
-
if (
|
|
1452
|
+
} else if (r.index === void 0) {
|
|
1453
|
+
if (i = n[r.key], i == null) return null;
|
|
1380
1454
|
continue;
|
|
1381
1455
|
}
|
|
1382
|
-
if (
|
|
1383
|
-
const o =
|
|
1456
|
+
if (i && r.index !== void 0 && i.items) {
|
|
1457
|
+
const o = i.items.value || i.items;
|
|
1384
1458
|
if (!Array.isArray(o)) return null;
|
|
1385
|
-
|
|
1386
|
-
} else if (
|
|
1459
|
+
i = o[r.index];
|
|
1460
|
+
} else if (i && r.index !== void 0 && !i.items)
|
|
1387
1461
|
return null;
|
|
1388
|
-
if (
|
|
1462
|
+
if (i == null) return null;
|
|
1389
1463
|
}
|
|
1390
|
-
return
|
|
1464
|
+
return i;
|
|
1391
1465
|
}
|
|
1392
1466
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
* ```
|
|
1410
|
-
*/
|
|
1411
|
-
set(e, t) {
|
|
1412
|
-
this.fields.set(e, t);
|
|
1413
|
-
}
|
|
1414
|
-
/**
|
|
1415
|
-
* Получить поле из реестра
|
|
1416
|
-
*
|
|
1417
|
-
* @param key - Ключ поля
|
|
1418
|
-
* @returns FormNode или undefined, если поле не найдено
|
|
1419
|
-
*
|
|
1420
|
-
* @example
|
|
1421
|
-
* ```typescript
|
|
1422
|
-
* const emailField = registry.get('email');
|
|
1423
|
-
* if (emailField) {
|
|
1424
|
-
* console.log(emailField.value.value);
|
|
1425
|
-
* }
|
|
1426
|
-
* ```
|
|
1427
|
-
*/
|
|
1428
|
-
get(e) {
|
|
1429
|
-
return this.fields.get(e);
|
|
1430
|
-
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Проверить наличие поля в реестре
|
|
1433
|
-
*
|
|
1434
|
-
* @param key - Ключ поля
|
|
1435
|
-
* @returns true если поле существует
|
|
1436
|
-
*
|
|
1437
|
-
* @example
|
|
1438
|
-
* ```typescript
|
|
1439
|
-
* if (registry.has('email')) {
|
|
1440
|
-
* console.log('Email field exists');
|
|
1441
|
-
* }
|
|
1442
|
-
* ```
|
|
1443
|
-
*/
|
|
1444
|
-
has(e) {
|
|
1445
|
-
return this.fields.has(e);
|
|
1446
|
-
}
|
|
1447
|
-
/**
|
|
1448
|
-
* Удалить поле из реестра
|
|
1449
|
-
*
|
|
1450
|
-
* @param key - Ключ поля
|
|
1451
|
-
* @returns true если поле было удалено, false если поля не было
|
|
1452
|
-
*
|
|
1453
|
-
* @example
|
|
1454
|
-
* ```typescript
|
|
1455
|
-
* registry.delete('email');
|
|
1456
|
-
* ```
|
|
1457
|
-
*/
|
|
1458
|
-
delete(e) {
|
|
1459
|
-
return this.fields.delete(e);
|
|
1460
|
-
}
|
|
1461
|
-
/**
|
|
1462
|
-
* Перебрать все поля
|
|
1463
|
-
*
|
|
1464
|
-
* @param callback - Функция обратного вызова для каждого поля
|
|
1465
|
-
*
|
|
1466
|
-
* @example
|
|
1467
|
-
* ```typescript
|
|
1468
|
-
* registry.forEach((field, key) => {
|
|
1469
|
-
* console.log(`${key}: ${field.value.value}`);
|
|
1470
|
-
* });
|
|
1471
|
-
* ```
|
|
1472
|
-
*/
|
|
1473
|
-
forEach(e) {
|
|
1474
|
-
this.fields.forEach(e);
|
|
1475
|
-
}
|
|
1476
|
-
/**
|
|
1477
|
-
* Получить итератор значений (полей)
|
|
1478
|
-
*
|
|
1479
|
-
* @returns Итератор по всем полям
|
|
1480
|
-
*
|
|
1481
|
-
* @example
|
|
1482
|
-
* ```typescript
|
|
1483
|
-
* for (const field of registry.values()) {
|
|
1484
|
-
* await field.validate();
|
|
1485
|
-
* }
|
|
1486
|
-
* ```
|
|
1487
|
-
*/
|
|
1488
|
-
values() {
|
|
1489
|
-
return this.fields.values();
|
|
1490
|
-
}
|
|
1491
|
-
/**
|
|
1492
|
-
* Получить итератор пар [ключ, значение]
|
|
1493
|
-
*
|
|
1494
|
-
* @returns Итератор по всем записям
|
|
1495
|
-
*
|
|
1496
|
-
* @example
|
|
1497
|
-
* ```typescript
|
|
1498
|
-
* for (const [key, field] of registry.entries()) {
|
|
1499
|
-
* console.log(key, field.value.value);
|
|
1500
|
-
* }
|
|
1501
|
-
* ```
|
|
1502
|
-
*/
|
|
1503
|
-
entries() {
|
|
1504
|
-
return this.fields.entries();
|
|
1505
|
-
}
|
|
1506
|
-
/**
|
|
1507
|
-
* Получить итератор ключей полей
|
|
1508
|
-
*
|
|
1509
|
-
* @returns Итератор по всем ключам
|
|
1510
|
-
*
|
|
1511
|
-
* @example
|
|
1512
|
-
* ```typescript
|
|
1513
|
-
* const fieldNames = Array.from(registry.keys());
|
|
1514
|
-
* // ['email', 'name', 'age']
|
|
1515
|
-
* ```
|
|
1516
|
-
*/
|
|
1517
|
-
keys() {
|
|
1518
|
-
return this.fields.keys();
|
|
1519
|
-
}
|
|
1520
|
-
/**
|
|
1521
|
-
* Получить количество полей
|
|
1522
|
-
*
|
|
1523
|
-
* @returns Количество зарегистрированных полей
|
|
1524
|
-
*
|
|
1525
|
-
* @example
|
|
1526
|
-
* ```typescript
|
|
1527
|
-
* console.log(`Form has ${registry.size()} fields`);
|
|
1528
|
-
* ```
|
|
1529
|
-
*/
|
|
1530
|
-
size() {
|
|
1531
|
-
return this.fields.size;
|
|
1532
|
-
}
|
|
1533
|
-
/**
|
|
1534
|
-
* Очистить все поля
|
|
1535
|
-
*
|
|
1536
|
-
* Удаляет все поля из реестра
|
|
1537
|
-
*
|
|
1538
|
-
* @example
|
|
1539
|
-
* ```typescript
|
|
1540
|
-
* registry.clear();
|
|
1541
|
-
* console.log(registry.size()); // 0
|
|
1542
|
-
* ```
|
|
1543
|
-
*/
|
|
1544
|
-
clear() {
|
|
1545
|
-
this.fields.clear();
|
|
1546
|
-
}
|
|
1547
|
-
/**
|
|
1548
|
-
* Получить все поля как массив
|
|
1549
|
-
*
|
|
1550
|
-
* Полезно для операций, требующих работу с массивом
|
|
1551
|
-
*
|
|
1552
|
-
* @returns Массив всех полей
|
|
1553
|
-
*
|
|
1554
|
-
* @example
|
|
1555
|
-
* ```typescript
|
|
1556
|
-
* const allValid = registry.toArray().every(field => field.valid.value);
|
|
1557
|
-
* ```
|
|
1558
|
-
*/
|
|
1559
|
-
toArray() {
|
|
1560
|
-
return Array.from(this.fields.values());
|
|
1561
|
-
}
|
|
1562
|
-
/**
|
|
1563
|
-
* Получить Map-представление реестра (readonly)
|
|
1564
|
-
*
|
|
1565
|
-
* Используйте для совместимости с существующим кодом
|
|
1566
|
-
*
|
|
1567
|
-
* @returns ReadonlyMap с полями
|
|
1568
|
-
* @internal
|
|
1569
|
-
*
|
|
1570
|
-
* @example
|
|
1571
|
-
* ```typescript
|
|
1572
|
-
* const mapView = registry.asMap();
|
|
1573
|
-
* ```
|
|
1574
|
-
*/
|
|
1575
|
-
asMap() {
|
|
1576
|
-
return this.fields;
|
|
1577
|
-
}
|
|
1467
|
+
function W(a) {
|
|
1468
|
+
const { getChildren: e, ownErrors: t, disabled: s } = a, i = d(() => t.value.length > 0 ? !1 : e().every((h) => h.disabled.value || h.valid.value)), r = d(() => !i.value), n = d(() => e().some((h) => h.pending.value)), o = d(() => e().some((h) => h.touched.value)), l = d(() => e().some((h) => h.dirty.value)), u = d(() => {
|
|
1469
|
+
const h = [...t.value];
|
|
1470
|
+
for (const f of e())
|
|
1471
|
+
h.push(...f.errors.value);
|
|
1472
|
+
return h;
|
|
1473
|
+
}), v = d(() => s?.value ? "disabled" : n.value ? "pending" : r.value ? "invalid" : "valid");
|
|
1474
|
+
return {
|
|
1475
|
+
valid: i,
|
|
1476
|
+
invalid: r,
|
|
1477
|
+
pending: n,
|
|
1478
|
+
touched: o,
|
|
1479
|
+
dirty: l,
|
|
1480
|
+
errors: u,
|
|
1481
|
+
status: v
|
|
1482
|
+
};
|
|
1578
1483
|
}
|
|
1579
|
-
|
|
1484
|
+
function ie(a) {
|
|
1485
|
+
return typeof a == "object" && a !== null && "getProxy" in a && typeof a.getProxy == "function";
|
|
1486
|
+
}
|
|
1487
|
+
function re(a, e) {
|
|
1488
|
+
return new Proxy(a, {
|
|
1489
|
+
get: (t, s) => {
|
|
1490
|
+
if (s in t)
|
|
1491
|
+
return t[s];
|
|
1492
|
+
if (typeof s == "string" && e.has(s)) {
|
|
1493
|
+
const i = e.get(s);
|
|
1494
|
+
return i && ie(i) ? i.getProxy() : i;
|
|
1495
|
+
}
|
|
1496
|
+
},
|
|
1497
|
+
set: (t, s, i) => typeof s == "string" && e.has(s) ? !1 : (t[s] = i, !0),
|
|
1498
|
+
has: (t, s) => typeof s == "string" && e.has(s) ? !0 : s in t,
|
|
1499
|
+
ownKeys: (t) => {
|
|
1500
|
+
const s = Reflect.ownKeys(t), i = Array.from(e.keys());
|
|
1501
|
+
return [.../* @__PURE__ */ new Set([...s, ...i])];
|
|
1502
|
+
},
|
|
1503
|
+
getOwnPropertyDescriptor: (t, s) => typeof s == "string" && e.has(s) ? { enumerable: !0, configurable: !0 } : Reflect.getOwnPropertyDescriptor(t, s)
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
class ae {
|
|
1580
1507
|
/**
|
|
1581
|
-
* @param
|
|
1508
|
+
* @param form - Форма для отправки
|
|
1582
1509
|
*/
|
|
1583
1510
|
constructor(e) {
|
|
1584
|
-
this.
|
|
1511
|
+
this.form = e;
|
|
1585
1512
|
}
|
|
1513
|
+
/** Внутренний сигнал состояния отправки */
|
|
1514
|
+
_submitting = g(!1);
|
|
1515
|
+
/** Публичный read-only сигнал состояния отправки */
|
|
1516
|
+
submitting = d(() => this._submitting.value);
|
|
1586
1517
|
/**
|
|
1587
|
-
*
|
|
1518
|
+
* Отправить форму
|
|
1588
1519
|
*
|
|
1589
|
-
*
|
|
1590
|
-
*
|
|
1591
|
-
*
|
|
1520
|
+
* Процесс:
|
|
1521
|
+
* 1. Помечает все поля как touched (для отображения ошибок)
|
|
1522
|
+
* 2. Валидирует форму
|
|
1523
|
+
* 3. Если валидация успешна - вызывает onSubmit
|
|
1524
|
+
* 4. Управляет состоянием submitting
|
|
1592
1525
|
*
|
|
1593
|
-
* @param
|
|
1594
|
-
* @
|
|
1526
|
+
* @param onSubmit - Callback для отправки данных
|
|
1527
|
+
* @param options - Опции submit
|
|
1528
|
+
* @returns Результат от onSubmit или null если валидация не пройдена
|
|
1595
1529
|
*
|
|
1596
1530
|
* @example
|
|
1597
1531
|
* ```typescript
|
|
1598
|
-
* const
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1602
|
-
*
|
|
1603
|
-
*
|
|
1604
|
-
*
|
|
1605
|
-
* await proxy.validate(); // Работает!
|
|
1606
|
-
* proxy.markAsTouched(); // Работает!
|
|
1607
|
-
*
|
|
1608
|
-
* // Проверка существования
|
|
1609
|
-
* if ('email' in proxy) { ... }
|
|
1532
|
+
* const result = await submitter.submit(async (values) => {
|
|
1533
|
+
* const response = await fetch('/api/form', {
|
|
1534
|
+
* method: 'POST',
|
|
1535
|
+
* body: JSON.stringify(values)
|
|
1536
|
+
* });
|
|
1537
|
+
* return response.json();
|
|
1538
|
+
* });
|
|
1610
1539
|
*
|
|
1611
|
-
*
|
|
1612
|
-
*
|
|
1540
|
+
* if (result === null) {
|
|
1541
|
+
* console.log('Форма не прошла валидацию');
|
|
1542
|
+
* }
|
|
1613
1543
|
* ```
|
|
1614
1544
|
*/
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
get: (t, i) => {
|
|
1626
|
-
if (i in t)
|
|
1627
|
-
return t[i];
|
|
1628
|
-
if (typeof i == "string" && this.fieldRegistry.has(i))
|
|
1629
|
-
return this.fieldRegistry.get(i);
|
|
1630
|
-
},
|
|
1631
|
-
/**
|
|
1632
|
-
* Set trap: Перехват установки свойств
|
|
1633
|
-
*
|
|
1634
|
-
* Запрещает прямую установку значений полей через form.email = value
|
|
1635
|
-
* Пользователь должен использовать form.email.setValue(value) или form.setValue({...})
|
|
1636
|
-
*/
|
|
1637
|
-
set: (t, i, s) => typeof i == "string" && this.fieldRegistry.has(i) ? !1 : (t[i] = s, !0),
|
|
1638
|
-
/**
|
|
1639
|
-
* Has trap: Перехват оператора 'in'
|
|
1640
|
-
*
|
|
1641
|
-
* Позволяет проверять существование полей:
|
|
1642
|
-
* if ('email' in form) { ... }
|
|
1643
|
-
*/
|
|
1644
|
-
has: (t, i) => typeof i == "string" && this.fieldRegistry.has(i) ? !0 : i in t,
|
|
1645
|
-
/**
|
|
1646
|
-
* OwnKeys trap: Перехват Object.keys() / Object.getOwnPropertyNames()
|
|
1647
|
-
*
|
|
1648
|
-
* Возвращает объединенный список:
|
|
1649
|
-
* - Ключей самого GroupNode
|
|
1650
|
-
* - Ключей полей из fieldRegistry
|
|
1651
|
-
*/
|
|
1652
|
-
ownKeys: (t) => {
|
|
1653
|
-
const i = Reflect.ownKeys(t), s = Array.from(this.fieldRegistry.keys());
|
|
1654
|
-
return [.../* @__PURE__ */ new Set([...i, ...s])];
|
|
1655
|
-
},
|
|
1656
|
-
/**
|
|
1657
|
-
* GetOwnPropertyDescriptor trap: Перехват Object.getOwnPropertyDescriptor()
|
|
1658
|
-
*
|
|
1659
|
-
* Возвращает дескриптор свойства для полей и свойств GroupNode
|
|
1660
|
-
* Важно для корректной работы Object.keys() и других рефлексивных операций
|
|
1661
|
-
*/
|
|
1662
|
-
getOwnPropertyDescriptor: (t, i) => typeof i == "string" && this.fieldRegistry.has(i) ? {
|
|
1663
|
-
enumerable: !0,
|
|
1664
|
-
// Поле должно быть перечисляемым
|
|
1665
|
-
configurable: !0
|
|
1666
|
-
// Поле может быть удалено
|
|
1667
|
-
// Не указываем writable, т.к. это accessor property через get/set traps
|
|
1668
|
-
} : Reflect.getOwnPropertyDescriptor(t, i)
|
|
1669
|
-
});
|
|
1545
|
+
async submit(e, t) {
|
|
1546
|
+
const { skipValidation: s = !1, skipTouch: i = !1 } = t || {};
|
|
1547
|
+
if (i || this.form.markAsTouched(), !s && !await this.form.validate())
|
|
1548
|
+
return null;
|
|
1549
|
+
this._submitting.value = !0;
|
|
1550
|
+
try {
|
|
1551
|
+
return await e(this.form.getValue());
|
|
1552
|
+
} finally {
|
|
1553
|
+
this._submitting.value = !1;
|
|
1554
|
+
}
|
|
1670
1555
|
}
|
|
1671
|
-
}
|
|
1672
|
-
class U {
|
|
1673
|
-
// ============================================================================
|
|
1674
|
-
// Конструктор
|
|
1675
|
-
// ============================================================================
|
|
1676
1556
|
/**
|
|
1677
|
-
*
|
|
1557
|
+
* Отправить форму с расширенным результатом
|
|
1678
1558
|
*
|
|
1679
|
-
*
|
|
1680
|
-
*/
|
|
1681
|
-
constructor(e) {
|
|
1682
|
-
this.fieldRegistry = e, this._submitting = d(!1), this._disabled = d(!1), this._formErrors = d([]), this.value = l(() => {
|
|
1683
|
-
const t = {};
|
|
1684
|
-
return this.fieldRegistry.forEach((i, s) => {
|
|
1685
|
-
t[s] = i.value.value;
|
|
1686
|
-
}), t;
|
|
1687
|
-
}), this.valid = l(() => this._formErrors.value.length > 0 ? !1 : Array.from(this.fieldRegistry.values()).every((i) => i.valid.value)), this.invalid = l(() => !this.valid.value), this.pending = l(
|
|
1688
|
-
() => Array.from(this.fieldRegistry.values()).some((t) => t.pending.value)
|
|
1689
|
-
), this.touched = l(
|
|
1690
|
-
() => Array.from(this.fieldRegistry.values()).some((t) => t.touched.value)
|
|
1691
|
-
), this.dirty = l(
|
|
1692
|
-
() => Array.from(this.fieldRegistry.values()).some((t) => t.dirty.value)
|
|
1693
|
-
), this.errors = l(() => {
|
|
1694
|
-
const t = [];
|
|
1695
|
-
return t.push(...this._formErrors.value), this.fieldRegistry.forEach((i) => {
|
|
1696
|
-
t.push(...i.errors.value);
|
|
1697
|
-
}), t;
|
|
1698
|
-
}), this.status = l(() => this._disabled.value ? "disabled" : this.pending.value ? "pending" : this.invalid.value ? "invalid" : "valid"), this.submitting = l(() => this._submitting.value);
|
|
1699
|
-
}
|
|
1700
|
-
// ============================================================================
|
|
1701
|
-
// Приватные сигналы (мутабельные)
|
|
1702
|
-
// ============================================================================
|
|
1703
|
-
/**
|
|
1704
|
-
* Флаг отправки формы
|
|
1705
|
-
* Устанавливается в true во время отправки формы на сервер
|
|
1706
|
-
*/
|
|
1707
|
-
_submitting;
|
|
1708
|
-
/**
|
|
1709
|
-
* Флаг disabled состояния
|
|
1710
|
-
* Если true, форма считается disabled
|
|
1711
|
-
*/
|
|
1712
|
-
_disabled;
|
|
1713
|
-
/**
|
|
1714
|
-
* Form-level validation errors (не связанные с конкретным полем)
|
|
1715
|
-
* Используется для server-side errors или кросс-полевой валидации
|
|
1716
|
-
*/
|
|
1717
|
-
_formErrors;
|
|
1718
|
-
// ============================================================================
|
|
1719
|
-
// Публичные computed signals (read-only)
|
|
1720
|
-
// ============================================================================
|
|
1721
|
-
/**
|
|
1722
|
-
* Значение формы как объект
|
|
1559
|
+
* В отличие от submit(), возвращает объект с информацией об успехе/ошибке
|
|
1723
1560
|
*
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1561
|
+
* @param onSubmit - Callback для отправки данных
|
|
1562
|
+
* @param options - Опции submit
|
|
1563
|
+
* @returns Объект SubmitResult с данными и статусом
|
|
1726
1564
|
*
|
|
1727
1565
|
* @example
|
|
1728
1566
|
* ```typescript
|
|
1729
|
-
* const
|
|
1730
|
-
*
|
|
1731
|
-
*
|
|
1732
|
-
*/
|
|
1733
|
-
value;
|
|
1734
|
-
/**
|
|
1735
|
-
* Форма валидна?
|
|
1736
|
-
*
|
|
1737
|
-
* Computed signal. Форма валидна, если:
|
|
1738
|
-
* - Нет form-level errors
|
|
1739
|
-
* - Все поля валидны
|
|
1740
|
-
*/
|
|
1741
|
-
valid;
|
|
1742
|
-
/**
|
|
1743
|
-
* Форма невалидна?
|
|
1744
|
-
*
|
|
1745
|
-
* Computed signal. Инверсия valid.
|
|
1746
|
-
*/
|
|
1747
|
-
invalid;
|
|
1748
|
-
/**
|
|
1749
|
-
* Хотя бы одно поле touched?
|
|
1750
|
-
*
|
|
1751
|
-
* Computed signal. Возвращает true, если хотя бы одно поле было touched.
|
|
1752
|
-
*/
|
|
1753
|
-
touched;
|
|
1754
|
-
/**
|
|
1755
|
-
* Хотя бы одно поле dirty?
|
|
1756
|
-
*
|
|
1757
|
-
* Computed signal. Возвращает true, если хотя бы одно поле изменилось.
|
|
1758
|
-
*/
|
|
1759
|
-
dirty;
|
|
1760
|
-
/**
|
|
1761
|
-
* Асинхронная валидация в процессе?
|
|
1762
|
-
*
|
|
1763
|
-
* Computed signal. Возвращает true, если хотя бы одно поле находится в pending состоянии.
|
|
1764
|
-
*/
|
|
1765
|
-
pending;
|
|
1766
|
-
/**
|
|
1767
|
-
* Все ошибки валидации
|
|
1768
|
-
*
|
|
1769
|
-
* Computed signal. Возвращает массив всех ошибок:
|
|
1770
|
-
* - Form-level errors
|
|
1771
|
-
* - Field-level errors (из всех вложенных полей)
|
|
1772
|
-
*/
|
|
1773
|
-
errors;
|
|
1774
|
-
/**
|
|
1775
|
-
* Общий статус формы
|
|
1776
|
-
*
|
|
1777
|
-
* Computed signal. Возможные значения:
|
|
1778
|
-
* - 'disabled' - форма disabled
|
|
1779
|
-
* - 'pending' - асинхронная валидация в процессе
|
|
1780
|
-
* - 'invalid' - форма невалидна
|
|
1781
|
-
* - 'valid' - форма валидна
|
|
1782
|
-
*/
|
|
1783
|
-
status;
|
|
1784
|
-
/**
|
|
1785
|
-
* Форма в процессе отправки?
|
|
1786
|
-
*
|
|
1787
|
-
* Computed signal (обертка над _submitting для read-only доступа).
|
|
1788
|
-
*/
|
|
1789
|
-
submitting;
|
|
1790
|
-
// ============================================================================
|
|
1791
|
-
// Публичные методы для управления состоянием
|
|
1792
|
-
// ============================================================================
|
|
1793
|
-
/**
|
|
1794
|
-
* Установить form-level ошибки
|
|
1795
|
-
*
|
|
1796
|
-
* @param errors - массив ошибок валидации
|
|
1797
|
-
*
|
|
1798
|
-
* @example
|
|
1799
|
-
* ```typescript
|
|
1800
|
-
* // Server-side ошибки
|
|
1801
|
-
* stateManager.setFormErrors([
|
|
1802
|
-
* { code: 'server_error', message: 'Пользователь с таким email уже существует' }
|
|
1803
|
-
* ]);
|
|
1804
|
-
* ```
|
|
1805
|
-
*/
|
|
1806
|
-
setFormErrors(e) {
|
|
1807
|
-
this._formErrors.value = e;
|
|
1808
|
-
}
|
|
1809
|
-
/**
|
|
1810
|
-
* Очистить form-level ошибки
|
|
1811
|
-
*/
|
|
1812
|
-
clearFormErrors() {
|
|
1813
|
-
this._formErrors.value = [];
|
|
1814
|
-
}
|
|
1815
|
-
/**
|
|
1816
|
-
* Получить form-level ошибки
|
|
1817
|
-
*/
|
|
1818
|
-
getFormErrors() {
|
|
1819
|
-
return this._formErrors.value;
|
|
1820
|
-
}
|
|
1821
|
-
/**
|
|
1822
|
-
* Установить флаг submitting
|
|
1823
|
-
*
|
|
1824
|
-
* @param value - true если форма отправляется, false если нет
|
|
1567
|
+
* const result = await submitter.submitWithResult(async (values) => {
|
|
1568
|
+
* return await api.saveForm(values);
|
|
1569
|
+
* });
|
|
1825
1570
|
*
|
|
1826
|
-
*
|
|
1827
|
-
*
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
1571
|
+
* if (result.success) {
|
|
1572
|
+
* console.log('Сохранено:', result.data);
|
|
1573
|
+
* } else if (result.error) {
|
|
1574
|
+
* console.error('Ошибка:', result.error.message);
|
|
1575
|
+
* } else {
|
|
1576
|
+
* console.log('Валидация не пройдена');
|
|
1577
|
+
* }
|
|
1831
1578
|
* ```
|
|
1832
1579
|
*/
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1580
|
+
async submitWithResult(e, t) {
|
|
1581
|
+
const { skipValidation: s = !1, skipTouch: i = !1 } = t || {};
|
|
1582
|
+
if (i || this.form.markAsTouched(), !s && !await this.form.validate())
|
|
1583
|
+
return { success: !1, data: null };
|
|
1584
|
+
this._submitting.value = !0;
|
|
1585
|
+
try {
|
|
1586
|
+
return { success: !0, data: await e(this.form.getValue()) };
|
|
1587
|
+
} catch (r) {
|
|
1588
|
+
return {
|
|
1589
|
+
success: !1,
|
|
1590
|
+
data: null,
|
|
1591
|
+
error: r instanceof Error ? r : new Error(String(r))
|
|
1592
|
+
};
|
|
1593
|
+
} finally {
|
|
1594
|
+
this._submitting.value = !1;
|
|
1595
|
+
}
|
|
1843
1596
|
}
|
|
1844
1597
|
/**
|
|
1845
|
-
*
|
|
1598
|
+
* Проверить, идет ли отправка формы
|
|
1846
1599
|
*/
|
|
1847
|
-
|
|
1848
|
-
return this.
|
|
1600
|
+
isSubmitting() {
|
|
1601
|
+
return this._submitting.value;
|
|
1849
1602
|
}
|
|
1850
1603
|
}
|
|
1851
|
-
class
|
|
1604
|
+
class k extends C {
|
|
1852
1605
|
// ============================================================================
|
|
1853
1606
|
// Приватные поля
|
|
1854
1607
|
// ============================================================================
|
|
1855
|
-
id =
|
|
1856
|
-
/**
|
|
1857
|
-
* Реестр полей формы
|
|
1858
|
-
* Использует FieldRegistry для инкапсуляции логики управления коллекцией полей
|
|
1859
|
-
*/
|
|
1860
|
-
fieldRegistry;
|
|
1861
|
-
/**
|
|
1862
|
-
* Строитель Proxy для типобезопасного доступа к полям
|
|
1863
|
-
* Использует ProxyBuilder для создания Proxy с расширенной функциональностью
|
|
1864
|
-
*/
|
|
1865
|
-
proxyBuilder;
|
|
1608
|
+
id = crypto.randomUUID();
|
|
1866
1609
|
/**
|
|
1867
|
-
*
|
|
1868
|
-
* Инкапсулирует всю логику создания и управления сигналами состояния
|
|
1869
|
-
* Извлечен из GroupNode для соблюдения SRP
|
|
1610
|
+
* Коллекция полей формы (упрощённый Map вместо FieldRegistry)
|
|
1870
1611
|
*/
|
|
1871
|
-
|
|
1612
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1613
|
+
_fields = /* @__PURE__ */ new Map();
|
|
1872
1614
|
/**
|
|
1873
1615
|
* Менеджер подписок для централизованного cleanup
|
|
1874
|
-
* Использует SubscriptionManager вместо массива для управления подписками
|
|
1875
1616
|
*/
|
|
1876
|
-
disposers = new
|
|
1617
|
+
disposers = new D();
|
|
1877
1618
|
/**
|
|
1878
1619
|
* Ссылка на Proxy-инстанс для использования в BehaviorContext
|
|
1879
|
-
* Устанавливается в конструкторе до применения behavior schema
|
|
1880
1620
|
*/
|
|
1881
1621
|
_proxyInstance;
|
|
1882
1622
|
/**
|
|
1883
1623
|
* Навигатор для работы с путями к полям
|
|
1884
|
-
* Использует композицию вместо дублирования логики парсинга путей
|
|
1885
1624
|
*/
|
|
1886
|
-
pathNavigator = new
|
|
1625
|
+
pathNavigator = new se();
|
|
1887
1626
|
/**
|
|
1888
1627
|
* Фабрика для создания узлов формы
|
|
1889
|
-
* Использует композицию для централизованного создания FieldNode/GroupNode/ArrayNode
|
|
1890
1628
|
*/
|
|
1891
|
-
nodeFactory = new
|
|
1629
|
+
nodeFactory = new ne();
|
|
1892
1630
|
/**
|
|
1893
1631
|
* Реестр валидаторов для этой формы
|
|
1894
|
-
*
|
|
1895
|
-
* Обеспечивает полную изоляцию форм друг от друга
|
|
1632
|
+
* Может быть инжектирован через config._validationRegistry для тестирования
|
|
1896
1633
|
*/
|
|
1897
|
-
validationRegistry
|
|
1634
|
+
validationRegistry;
|
|
1898
1635
|
/**
|
|
1899
1636
|
* Реестр behaviors для этой формы
|
|
1900
|
-
*
|
|
1901
|
-
* Обеспечивает полную изоляцию форм друг от друга
|
|
1637
|
+
* Может быть инжектирован через config._behaviorRegistry для тестирования
|
|
1902
1638
|
*/
|
|
1903
|
-
behaviorRegistry
|
|
1639
|
+
behaviorRegistry;
|
|
1904
1640
|
/**
|
|
1905
1641
|
* Аппликатор для применения валидаторов к форме
|
|
1906
|
-
* Извлечен из GroupNode для соблюдения SRP
|
|
1907
|
-
* Использует композицию для управления процессом валидации
|
|
1908
|
-
*/
|
|
1909
|
-
validationApplicator = new N(this);
|
|
1910
|
-
/**
|
|
1911
|
-
* Аппликатор для применения behavior схемы к форме
|
|
1912
|
-
* Извлечен из GroupNode для соблюдения SRP
|
|
1913
|
-
* Использует композицию для управления процессом применения behaviors
|
|
1914
1642
|
*/
|
|
1915
|
-
|
|
1643
|
+
validationApplicator = new te(this);
|
|
1916
1644
|
// ============================================================================
|
|
1917
|
-
//
|
|
1645
|
+
// Приватные сигналы состояния (inline из StateManager)
|
|
1646
|
+
// ============================================================================
|
|
1647
|
+
/** Управление отправкой формы */
|
|
1648
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1649
|
+
formSubmitter;
|
|
1650
|
+
/** Флаг disabled состояния */
|
|
1651
|
+
_disabled = g(!1);
|
|
1652
|
+
/** Form-level validation errors */
|
|
1653
|
+
_formErrors = g([]);
|
|
1654
|
+
// ============================================================================
|
|
1655
|
+
// Публичные computed signals
|
|
1918
1656
|
// ============================================================================
|
|
1919
1657
|
value;
|
|
1920
1658
|
valid;
|
|
@@ -1926,36 +1664,58 @@ class m extends A {
|
|
|
1926
1664
|
status;
|
|
1927
1665
|
submitting;
|
|
1928
1666
|
constructor(e) {
|
|
1929
|
-
super(), this.
|
|
1930
|
-
const t = "form" in e, i = t ? e.form : e,
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
this.
|
|
1667
|
+
super(), this.formSubmitter = new ae(this);
|
|
1668
|
+
const t = "form" in e, s = t ? e : void 0, i = t ? e.form : e, r = t ? e.behavior : void 0, n = t ? e.validation : void 0;
|
|
1669
|
+
this.validationRegistry = s?._validationRegistry ?? new K(), this.behaviorRegistry = s?._behaviorRegistry ?? new H();
|
|
1670
|
+
for (const [l, u] of Object.entries(i)) {
|
|
1671
|
+
const v = this.createNode(u);
|
|
1672
|
+
this._fields.set(l, v);
|
|
1934
1673
|
}
|
|
1935
|
-
this.
|
|
1936
|
-
|
|
1937
|
-
|
|
1674
|
+
this.value = d(() => {
|
|
1675
|
+
const l = {};
|
|
1676
|
+
return this._fields.forEach((u, v) => {
|
|
1677
|
+
l[v] = u.value.value;
|
|
1678
|
+
}), l;
|
|
1679
|
+
});
|
|
1680
|
+
const o = W({
|
|
1681
|
+
getChildren: () => Array.from(this._fields.values()),
|
|
1682
|
+
ownErrors: this._formErrors,
|
|
1683
|
+
disabled: this._disabled
|
|
1684
|
+
});
|
|
1685
|
+
this.valid = o.valid, this.invalid = o.invalid, this.pending = o.pending, this.touched = o.touched, this.dirty = o.dirty, this.errors = o.errors, this.status = o.status, this.submitting = this.formSubmitter.submitting, r && this.applyBehaviorSchema(r), n && this.applyValidationSchema(n);
|
|
1686
|
+
}
|
|
1687
|
+
// ============================================================================
|
|
1688
|
+
// Приватный метод для создания Proxy
|
|
1689
|
+
// ============================================================================
|
|
1690
|
+
/**
|
|
1691
|
+
* Создать Proxy для типобезопасного доступа к полям
|
|
1692
|
+
* @see buildFormProxy
|
|
1693
|
+
*/
|
|
1694
|
+
buildProxy() {
|
|
1695
|
+
return re(this, this._fields);
|
|
1938
1696
|
}
|
|
1939
1697
|
// ============================================================================
|
|
1940
1698
|
// Реализация абстрактных методов FormNode
|
|
1941
1699
|
// ============================================================================
|
|
1942
1700
|
getValue() {
|
|
1943
1701
|
const e = {};
|
|
1944
|
-
return this.
|
|
1945
|
-
e[
|
|
1702
|
+
return this._fields.forEach((t, s) => {
|
|
1703
|
+
e[s] = t.getValue();
|
|
1946
1704
|
}), e;
|
|
1947
1705
|
}
|
|
1948
1706
|
setValue(e, t) {
|
|
1949
|
-
for (const [
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1707
|
+
for (const [s, i] of Object.entries(e)) {
|
|
1708
|
+
const r = this._fields.get(s);
|
|
1709
|
+
r && r.setValue(i, t);
|
|
1952
1710
|
}
|
|
1953
1711
|
}
|
|
1954
1712
|
patchValue(e) {
|
|
1955
|
-
|
|
1956
|
-
const s
|
|
1957
|
-
|
|
1958
|
-
|
|
1713
|
+
j(() => {
|
|
1714
|
+
for (const [t, s] of Object.entries(e)) {
|
|
1715
|
+
const i = this._fields.get(t);
|
|
1716
|
+
i && s !== void 0 && i.setValue(s, { emitEvent: !1 });
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1959
1719
|
}
|
|
1960
1720
|
/**
|
|
1961
1721
|
* Сбросить форму к указанным значениям (или к initialValues)
|
|
@@ -1975,103 +1735,49 @@ class m extends A {
|
|
|
1975
1735
|
* ```
|
|
1976
1736
|
*/
|
|
1977
1737
|
reset(e) {
|
|
1978
|
-
this.
|
|
1979
|
-
const
|
|
1980
|
-
t.reset(
|
|
1738
|
+
this._fields.forEach((t, s) => {
|
|
1739
|
+
const i = e?.[s];
|
|
1740
|
+
t.reset(i);
|
|
1981
1741
|
});
|
|
1982
1742
|
}
|
|
1983
1743
|
/**
|
|
1984
1744
|
* Сбросить форму к исходным значениям (initialValues)
|
|
1985
|
-
*
|
|
1986
|
-
* @remarks
|
|
1987
|
-
* Рекурсивно вызывает resetToInitial() для всех полей формы.
|
|
1988
|
-
* Более явный способ сброса к начальным значениям по сравнению с reset()
|
|
1989
|
-
*
|
|
1990
|
-
* Полезно когда:
|
|
1991
|
-
* - Пользователь нажал "Cancel" - полная отмена изменений
|
|
1992
|
-
* - Форма была изменена через reset(newValues), но нужно вернуться к самому началу
|
|
1993
|
-
* - Явное намерение показать "отмена всех изменений"
|
|
1994
|
-
*
|
|
1995
|
-
* @example
|
|
1996
|
-
* ```typescript
|
|
1997
|
-
* const form = new GroupNode({
|
|
1998
|
-
* email: { value: 'initial@mail.com', component: Input },
|
|
1999
|
-
* name: { value: 'John', component: Input }
|
|
2000
|
-
* });
|
|
2001
|
-
*
|
|
2002
|
-
* form.email.setValue('changed@mail.com');
|
|
2003
|
-
* form.reset({ email: 'temp@mail.com', name: 'Jane' });
|
|
2004
|
-
* console.log(form.getValue()); // { email: 'temp@mail.com', name: 'Jane' }
|
|
2005
|
-
*
|
|
2006
|
-
* form.resetToInitial();
|
|
2007
|
-
* console.log(form.getValue()); // { email: 'initial@mail.com', name: 'John' }
|
|
2008
|
-
* ```
|
|
2009
1745
|
*/
|
|
2010
1746
|
resetToInitial() {
|
|
2011
|
-
this.
|
|
1747
|
+
this._fields.forEach((e) => {
|
|
2012
1748
|
"resetToInitial" in e && typeof e.resetToInitial == "function" ? e.resetToInitial() : e.reset();
|
|
2013
1749
|
});
|
|
2014
1750
|
}
|
|
2015
1751
|
async validate() {
|
|
2016
|
-
this.clearErrors(), await Promise.all(Array.from(this.
|
|
1752
|
+
this.clearErrors(), await Promise.all(Array.from(this._fields.values()).map((t) => t.validate()));
|
|
2017
1753
|
const e = this.validationRegistry.getValidators();
|
|
2018
|
-
return e && e.length > 0 && await this.applyContextualValidators(e), Array.from(this.
|
|
1754
|
+
return e && e.length > 0 && await this.applyContextualValidators(e), Array.from(this._fields.values()).every(
|
|
1755
|
+
(t) => t.valid.value || t.disabled.value
|
|
1756
|
+
);
|
|
2019
1757
|
}
|
|
2020
1758
|
/**
|
|
2021
1759
|
* Установить form-level validation errors
|
|
2022
|
-
* Используется для server-side validation или кросс-полевых ошибок
|
|
2023
|
-
*
|
|
2024
|
-
* @param errors - массив ошибок уровня формы
|
|
2025
|
-
*
|
|
2026
|
-
* @example
|
|
2027
|
-
* ```typescript
|
|
2028
|
-
* // Server-side validation после submit
|
|
2029
|
-
* try {
|
|
2030
|
-
* await api.createUser(form.getValue());
|
|
2031
|
-
* } catch (error) {
|
|
2032
|
-
* form.setErrors([
|
|
2033
|
-
* { code: 'duplicate_email', message: 'Email уже используется' }
|
|
2034
|
-
* ]);
|
|
2035
|
-
* }
|
|
2036
|
-
* ```
|
|
2037
1760
|
*/
|
|
2038
1761
|
setErrors(e) {
|
|
2039
|
-
this.
|
|
1762
|
+
this._formErrors.value = e;
|
|
2040
1763
|
}
|
|
2041
1764
|
/**
|
|
2042
1765
|
* Очистить все errors (form-level + field-level)
|
|
2043
1766
|
*/
|
|
2044
1767
|
clearErrors() {
|
|
2045
|
-
this.
|
|
1768
|
+
this._formErrors.value = [], this._fields.forEach((e) => e.clearErrors());
|
|
2046
1769
|
}
|
|
2047
1770
|
/**
|
|
2048
1771
|
* Получить поле по ключу
|
|
2049
|
-
*
|
|
2050
|
-
* Публичный метод для доступа к полю из fieldRegistry
|
|
2051
|
-
*
|
|
2052
|
-
* @param key - Ключ поля
|
|
2053
|
-
* @returns FormNode или undefined, если поле не найдено
|
|
2054
|
-
*
|
|
2055
|
-
* @example
|
|
2056
|
-
* ```typescript
|
|
2057
|
-
* const emailField = form.getField('email');
|
|
2058
|
-
* if (emailField) {
|
|
2059
|
-
* console.log(emailField.value.value);
|
|
2060
|
-
* }
|
|
2061
|
-
* ```
|
|
2062
1772
|
*/
|
|
2063
1773
|
getField(e) {
|
|
2064
|
-
return this.
|
|
1774
|
+
return this._fields.get(e);
|
|
2065
1775
|
}
|
|
2066
1776
|
/**
|
|
2067
|
-
* Получить Map всех полей формы
|
|
2068
|
-
*
|
|
2069
|
-
* Используется в FieldPathNavigator для навигации по полям
|
|
2070
|
-
*
|
|
2071
|
-
* @returns Map полей формы
|
|
1777
|
+
* Получить Map всех полей формы (для совместимости)
|
|
2072
1778
|
*/
|
|
2073
1779
|
get fields() {
|
|
2074
|
-
return this.
|
|
1780
|
+
return this._fields;
|
|
2075
1781
|
}
|
|
2076
1782
|
/**
|
|
2077
1783
|
* Получить Proxy-инстанс для прямого доступа к полям
|
|
@@ -2100,77 +1806,51 @@ class m extends A {
|
|
|
2100
1806
|
* ```
|
|
2101
1807
|
*/
|
|
2102
1808
|
getProxy() {
|
|
2103
|
-
return this._proxyInstance || this;
|
|
1809
|
+
return this._proxyInstance || (this._proxyInstance = this.buildProxy()), this._proxyInstance;
|
|
2104
1810
|
}
|
|
2105
1811
|
/**
|
|
2106
1812
|
* Получить все поля формы как итератор
|
|
2107
|
-
*
|
|
2108
|
-
* Предоставляет доступ к внутренним полям для валидации и других операций
|
|
2109
|
-
*
|
|
2110
|
-
* @returns Итератор по всем полям формы
|
|
2111
|
-
*
|
|
2112
|
-
* @example
|
|
2113
|
-
* ```typescript
|
|
2114
|
-
* // Валидация всех полей
|
|
2115
|
-
* await Promise.all(
|
|
2116
|
-
* Array.from(form.getAllFields()).map(field => field.validate())
|
|
2117
|
-
* );
|
|
2118
|
-
* ```
|
|
2119
1813
|
*/
|
|
2120
1814
|
getAllFields() {
|
|
2121
|
-
return this.
|
|
1815
|
+
return this._fields.values();
|
|
2122
1816
|
}
|
|
2123
1817
|
// ============================================================================
|
|
2124
1818
|
// Protected hooks (Template Method pattern)
|
|
2125
1819
|
// ============================================================================
|
|
2126
|
-
/**
|
|
2127
|
-
* Hook: вызывается после markAsTouched()
|
|
2128
|
-
*
|
|
2129
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как touched
|
|
2130
|
-
*/
|
|
2131
1820
|
onMarkAsTouched() {
|
|
2132
|
-
this.
|
|
1821
|
+
this._fields.forEach((e) => e.markAsTouched());
|
|
2133
1822
|
}
|
|
2134
|
-
/**
|
|
2135
|
-
* Hook: вызывается после markAsUntouched()
|
|
2136
|
-
*
|
|
2137
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как untouched
|
|
2138
|
-
*/
|
|
2139
1823
|
onMarkAsUntouched() {
|
|
2140
|
-
this.
|
|
1824
|
+
this._fields.forEach((e) => e.markAsUntouched());
|
|
2141
1825
|
}
|
|
2142
|
-
/**
|
|
2143
|
-
* Hook: вызывается после markAsDirty()
|
|
2144
|
-
*
|
|
2145
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как dirty
|
|
2146
|
-
*/
|
|
2147
1826
|
onMarkAsDirty() {
|
|
2148
|
-
this.
|
|
1827
|
+
this._fields.forEach((e) => e.markAsDirty());
|
|
2149
1828
|
}
|
|
2150
|
-
/**
|
|
2151
|
-
* Hook: вызывается после markAsPristine()
|
|
2152
|
-
*
|
|
2153
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как pristine
|
|
2154
|
-
*/
|
|
2155
1829
|
onMarkAsPristine() {
|
|
2156
|
-
this.
|
|
1830
|
+
this._fields.forEach((e) => e.markAsPristine());
|
|
2157
1831
|
}
|
|
2158
1832
|
// ============================================================================
|
|
2159
1833
|
// Дополнительные методы (из FormStore)
|
|
2160
1834
|
// ============================================================================
|
|
2161
1835
|
/**
|
|
2162
1836
|
* Отправить форму
|
|
2163
|
-
*
|
|
1837
|
+
*
|
|
1838
|
+
* @param onSubmit - Callback для отправки данных
|
|
1839
|
+
* @param options - Опции submit (skipValidation, skipTouch)
|
|
1840
|
+
* @returns Результат от onSubmit или null если валидация не пройдена
|
|
2164
1841
|
*/
|
|
2165
|
-
async submit(e) {
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
1842
|
+
async submit(e, t) {
|
|
1843
|
+
return this.formSubmitter.submit(e, t);
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Отправить форму с расширенным результатом
|
|
1847
|
+
*
|
|
1848
|
+
* @param onSubmit - Callback для отправки данных
|
|
1849
|
+
* @param options - Опции submit
|
|
1850
|
+
* @returns Объект SubmitResult с данными, статусом и возможной ошибкой
|
|
1851
|
+
*/
|
|
1852
|
+
async submitWithResult(e, t) {
|
|
1853
|
+
return this.formSubmitter.submitWithResult(e, t);
|
|
2174
1854
|
}
|
|
2175
1855
|
/**
|
|
2176
1856
|
* Применить validation schema к форме
|
|
@@ -2181,54 +1861,26 @@ class m extends A {
|
|
|
2181
1861
|
applyValidationSchema(e) {
|
|
2182
1862
|
this.validationRegistry.beginRegistration();
|
|
2183
1863
|
try {
|
|
2184
|
-
const t =
|
|
1864
|
+
const t = I();
|
|
2185
1865
|
e(t);
|
|
2186
|
-
const
|
|
2187
|
-
this.validationRegistry.endRegistration(
|
|
1866
|
+
const s = this.getProxy();
|
|
1867
|
+
this.validationRegistry.endRegistration(s);
|
|
2188
1868
|
} catch (t) {
|
|
2189
1869
|
throw console.error("Error applying validation schema:", t), t;
|
|
2190
1870
|
}
|
|
2191
1871
|
}
|
|
2192
1872
|
/**
|
|
2193
1873
|
* Применить behavior schema к форме
|
|
2194
|
-
*
|
|
2195
|
-
* ✅ РЕФАКТОРИНГ: Делегирование BehaviorApplicator (SRP)
|
|
2196
|
-
*
|
|
2197
|
-
* Логика применения behavior схемы извлечена в BehaviorApplicator для:
|
|
2198
|
-
* - Соблюдения Single Responsibility Principle
|
|
2199
|
-
* - Уменьшения размера GroupNode (~50 строк)
|
|
2200
|
-
* - Улучшения тестируемости
|
|
2201
|
-
* - Консистентности с ValidationApplicator
|
|
2202
|
-
*
|
|
2203
|
-
* @param schemaFn Функция описания поведения формы
|
|
2204
1874
|
* @returns Функция cleanup для отписки от всех behaviors
|
|
2205
|
-
*
|
|
2206
|
-
* @example
|
|
2207
|
-
* ```typescript
|
|
2208
|
-
* import { copyFrom, enableWhen, computeFrom } from '@/lib/forms/core/behaviors';
|
|
2209
|
-
*
|
|
2210
|
-
* const behaviorSchema: BehaviorSchemaFn<MyForm> = (path) => {
|
|
2211
|
-
* copyFrom(path.residenceAddress, path.registrationAddress, {
|
|
2212
|
-
* when: (form) => form.sameAsRegistration === true
|
|
2213
|
-
* });
|
|
2214
|
-
*
|
|
2215
|
-
* enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
|
|
2216
|
-
*
|
|
2217
|
-
* computeFrom(
|
|
2218
|
-
* path.initialPayment,
|
|
2219
|
-
* [path.propertyValue],
|
|
2220
|
-
* (propertyValue) => propertyValue ? propertyValue * 0.2 : null
|
|
2221
|
-
* );
|
|
2222
|
-
* };
|
|
2223
|
-
*
|
|
2224
|
-
* const cleanup = form.applyBehaviorSchema(behaviorSchema);
|
|
2225
|
-
*
|
|
2226
|
-
* // Cleanup при unmount
|
|
2227
|
-
* useEffect(() => cleanup, []);
|
|
2228
|
-
* ```
|
|
2229
1875
|
*/
|
|
2230
1876
|
applyBehaviorSchema(e) {
|
|
2231
|
-
|
|
1877
|
+
this.behaviorRegistry.beginRegistration();
|
|
1878
|
+
try {
|
|
1879
|
+
const t = I();
|
|
1880
|
+
return e(t), this.behaviorRegistry.endRegistration(this.getProxy()).cleanup;
|
|
1881
|
+
} catch (t) {
|
|
1882
|
+
throw console.error("Error applying behavior schema:", t), t;
|
|
1883
|
+
}
|
|
2232
1884
|
}
|
|
2233
1885
|
/**
|
|
2234
1886
|
* Получить вложенное поле по пути
|
|
@@ -2265,18 +1917,18 @@ class m extends A {
|
|
|
2265
1917
|
const t = this.pathNavigator.parsePath(e);
|
|
2266
1918
|
if (t.length === 0)
|
|
2267
1919
|
return;
|
|
2268
|
-
let
|
|
2269
|
-
for (const
|
|
2270
|
-
if (!(
|
|
2271
|
-
if (
|
|
2272
|
-
if ("at" in
|
|
2273
|
-
const
|
|
2274
|
-
if (!
|
|
2275
|
-
|
|
1920
|
+
let s = this;
|
|
1921
|
+
for (const i of t) {
|
|
1922
|
+
if (!(s instanceof k) || (s = s.getField(i.key), !s)) return;
|
|
1923
|
+
if (i.index !== void 0)
|
|
1924
|
+
if ("at" in s && "length" in s && typeof s.at == "function") {
|
|
1925
|
+
const r = s.at(i.index);
|
|
1926
|
+
if (!r) return;
|
|
1927
|
+
s = r;
|
|
2276
1928
|
} else
|
|
2277
1929
|
return;
|
|
2278
1930
|
}
|
|
2279
|
-
return
|
|
1931
|
+
return s;
|
|
2280
1932
|
}
|
|
2281
1933
|
/**
|
|
2282
1934
|
* Применить contextual валидаторы к полям
|
|
@@ -2319,39 +1971,17 @@ class m extends A {
|
|
|
2319
1971
|
// ============================================================================
|
|
2320
1972
|
/**
|
|
2321
1973
|
* Связывает два поля: при изменении source автоматически обновляется target
|
|
2322
|
-
* Поддерживает опциональную трансформацию значения
|
|
2323
|
-
*
|
|
2324
|
-
* @param sourceKey - Ключ поля-источника
|
|
2325
|
-
* @param targetKey - Ключ поля-цели
|
|
2326
|
-
* @param transform - Опциональная функция трансформации значения
|
|
2327
|
-
* @returns Функция отписки для cleanup
|
|
2328
|
-
*
|
|
2329
|
-
* @example
|
|
2330
|
-
* ```typescript
|
|
2331
|
-
* // Автоматический расчет минимального взноса от стоимости недвижимости
|
|
2332
|
-
* const dispose = form.linkFields(
|
|
2333
|
-
* 'propertyValue',
|
|
2334
|
-
* 'initialPayment',
|
|
2335
|
-
* (propertyValue) => propertyValue ? propertyValue * 0.2 : null
|
|
2336
|
-
* );
|
|
2337
|
-
*
|
|
2338
|
-
* // При изменении propertyValue → автоматически обновится initialPayment
|
|
2339
|
-
* form.propertyValue.setValue(1000000);
|
|
2340
|
-
* // initialPayment станет 200000
|
|
2341
|
-
*
|
|
2342
|
-
* // Cleanup
|
|
2343
|
-
* useEffect(() => dispose, []);
|
|
2344
|
-
* ```
|
|
2345
1974
|
*/
|
|
2346
|
-
linkFields(e, t,
|
|
2347
|
-
const
|
|
2348
|
-
if (!
|
|
2349
|
-
|
|
2350
|
-
};
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
1975
|
+
linkFields(e, t, s) {
|
|
1976
|
+
const i = this._fields.get(e), r = this._fields.get(t);
|
|
1977
|
+
if (!i || !r) {
|
|
1978
|
+
const l = i ? t : e;
|
|
1979
|
+
throw new Error(`GroupNode.linkFields: field "${String(l)}" not found`);
|
|
1980
|
+
}
|
|
1981
|
+
const n = m(() => {
|
|
1982
|
+
const l = i.value.value, u = s ? s(l) : l;
|
|
1983
|
+
r.setValue(u, { emitEvent: !1 });
|
|
1984
|
+
}), o = A(w.LinkFields);
|
|
2355
1985
|
return this.disposers.add(o, n);
|
|
2356
1986
|
}
|
|
2357
1987
|
/**
|
|
@@ -2382,56 +2012,37 @@ class m extends A {
|
|
|
2382
2012
|
* ```
|
|
2383
2013
|
*/
|
|
2384
2014
|
watchField(e, t) {
|
|
2385
|
-
const
|
|
2386
|
-
if (!
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
const n = i.value.value;
|
|
2015
|
+
const s = this.getFieldByPath(e);
|
|
2016
|
+
if (!s)
|
|
2017
|
+
throw new Error(`GroupNode.watchField: field "${e}" not found`);
|
|
2018
|
+
const i = m(() => {
|
|
2019
|
+
const n = s.value.value;
|
|
2391
2020
|
t(n);
|
|
2392
|
-
}),
|
|
2393
|
-
return this.disposers.add(
|
|
2021
|
+
}), r = A(w.WatchField);
|
|
2022
|
+
return this.disposers.add(r, i);
|
|
2394
2023
|
}
|
|
2395
2024
|
/**
|
|
2396
2025
|
* Hook: вызывается после disable()
|
|
2397
|
-
*
|
|
2398
|
-
* Для GroupNode: рекурсивно отключаем все дочерние поля
|
|
2399
2026
|
*/
|
|
2400
2027
|
onDisable() {
|
|
2401
|
-
this.
|
|
2402
|
-
e.disable();
|
|
2403
|
-
});
|
|
2028
|
+
this._disabled.value = !0, this._fields.forEach((e) => e.disable());
|
|
2404
2029
|
}
|
|
2405
2030
|
/**
|
|
2406
2031
|
* Hook: вызывается после enable()
|
|
2407
|
-
*
|
|
2408
|
-
* Для GroupNode: рекурсивно включаем все дочерние поля
|
|
2409
2032
|
*/
|
|
2410
2033
|
onEnable() {
|
|
2411
|
-
this.
|
|
2412
|
-
e.enable();
|
|
2413
|
-
});
|
|
2034
|
+
this._disabled.value = !1, this._fields.forEach((e) => e.enable());
|
|
2414
2035
|
}
|
|
2415
2036
|
/**
|
|
2416
2037
|
* Очистить все ресурсы узла
|
|
2417
|
-
* Рекурсивно очищает все subscriptions и дочерние узлы
|
|
2418
|
-
*
|
|
2419
|
-
* @example
|
|
2420
|
-
* ```typescript
|
|
2421
|
-
* useEffect(() => {
|
|
2422
|
-
* return () => {
|
|
2423
|
-
* form.dispose();
|
|
2424
|
-
* };
|
|
2425
|
-
* }, []);
|
|
2426
|
-
* ```
|
|
2427
2038
|
*/
|
|
2428
2039
|
dispose() {
|
|
2429
|
-
this.disposers.dispose(), this.
|
|
2040
|
+
this.disposers.dispose(), this._fields.forEach((e) => {
|
|
2430
2041
|
"dispose" in e && typeof e.dispose == "function" && e.dispose();
|
|
2431
2042
|
});
|
|
2432
2043
|
}
|
|
2433
2044
|
}
|
|
2434
|
-
class
|
|
2045
|
+
class L extends C {
|
|
2435
2046
|
// ============================================================================
|
|
2436
2047
|
// Приватные поля
|
|
2437
2048
|
// ============================================================================
|
|
@@ -2442,7 +2053,9 @@ class F extends A {
|
|
|
2442
2053
|
* Менеджер подписок для централизованного cleanup
|
|
2443
2054
|
* Использует SubscriptionManager вместо массива для управления подписками
|
|
2444
2055
|
*/
|
|
2445
|
-
disposers = new
|
|
2056
|
+
disposers = new D();
|
|
2057
|
+
/** Array-level validation errors (e.g., "минимум 1 элемент") */
|
|
2058
|
+
_arrayErrors = g([]);
|
|
2446
2059
|
// ============================================================================
|
|
2447
2060
|
// Приватные поля для сохранения схем
|
|
2448
2061
|
// ============================================================================
|
|
@@ -2464,15 +2077,15 @@ class F extends A {
|
|
|
2464
2077
|
// Конструктор
|
|
2465
2078
|
// ============================================================================
|
|
2466
2079
|
constructor(e, t = []) {
|
|
2467
|
-
super(), this.itemSchema = e, this.initialItems = t, this.items =
|
|
2080
|
+
super(), this.itemSchema = e, this.initialItems = t, this.items = g([]);
|
|
2468
2081
|
for (const i of t)
|
|
2469
2082
|
this.push(i);
|
|
2470
|
-
this.length =
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2083
|
+
this.length = d(() => this.items.value.length), this.value = d(() => this.items.value.map((i) => i.value.value));
|
|
2084
|
+
const s = W({
|
|
2085
|
+
getChildren: () => this.items.value,
|
|
2086
|
+
ownErrors: this._arrayErrors
|
|
2087
|
+
});
|
|
2088
|
+
this.valid = s.valid, this.invalid = s.invalid, this.pending = s.pending, this.touched = s.touched, this.dirty = s.dirty, this.errors = s.errors, this.status = s.status;
|
|
2476
2089
|
}
|
|
2477
2090
|
// ============================================================================
|
|
2478
2091
|
// CRUD операции
|
|
@@ -2488,9 +2101,23 @@ class F extends A {
|
|
|
2488
2101
|
/**
|
|
2489
2102
|
* Удалить элемент по индексу
|
|
2490
2103
|
* @param index - Индекс элемента для удаления
|
|
2104
|
+
*
|
|
2105
|
+
* @remarks
|
|
2106
|
+
* Вызывает dispose() на удаляемом элементе для очистки подписок
|
|
2491
2107
|
*/
|
|
2492
2108
|
removeAt(e) {
|
|
2493
|
-
e < 0 || e >= this.items.value.length
|
|
2109
|
+
if (e < 0 || e >= this.items.value.length) {
|
|
2110
|
+
S.handle(
|
|
2111
|
+
new Error(
|
|
2112
|
+
`ArrayNode.removeAt: index ${e} out of bounds (length: ${this.items.value.length})`
|
|
2113
|
+
),
|
|
2114
|
+
"ArrayNode.removeAt",
|
|
2115
|
+
V.LOG
|
|
2116
|
+
);
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
const t = this.items.value[e];
|
|
2120
|
+
this.items.value = this.items.value.filter((s, i) => i !== e), t && "dispose" in t && typeof t.dispose == "function" && t.dispose();
|
|
2494
2121
|
}
|
|
2495
2122
|
/**
|
|
2496
2123
|
* Вставить элемент в массив
|
|
@@ -2498,24 +2125,40 @@ class F extends A {
|
|
|
2498
2125
|
* @param initialValue - Начальные значения для нового элемента
|
|
2499
2126
|
*/
|
|
2500
2127
|
insert(e, t) {
|
|
2501
|
-
if (e < 0 || e > this.items.value.length)
|
|
2128
|
+
if (e < 0 || e > this.items.value.length) {
|
|
2129
|
+
S.handle(
|
|
2130
|
+
new Error(
|
|
2131
|
+
`ArrayNode.insert: index ${e} out of bounds (length: ${this.items.value.length})`
|
|
2132
|
+
),
|
|
2133
|
+
"ArrayNode.insert",
|
|
2134
|
+
V.LOG
|
|
2135
|
+
);
|
|
2502
2136
|
return;
|
|
2503
|
-
|
|
2504
|
-
s.
|
|
2137
|
+
}
|
|
2138
|
+
const s = this.createItem(t), i = [...this.items.value];
|
|
2139
|
+
i.splice(e, 0, s), this.items.value = i;
|
|
2505
2140
|
}
|
|
2506
2141
|
/**
|
|
2507
2142
|
* Удалить все элементы массива
|
|
2143
|
+
*
|
|
2144
|
+
* @remarks
|
|
2145
|
+
* Вызывает dispose() на всех элементах для очистки подписок
|
|
2508
2146
|
*/
|
|
2509
2147
|
clear() {
|
|
2510
|
-
this.items.value
|
|
2148
|
+
const e = [...this.items.value];
|
|
2149
|
+
this.items.value = [], e.forEach((t) => {
|
|
2150
|
+
"dispose" in t && typeof t.dispose == "function" && t.dispose();
|
|
2151
|
+
});
|
|
2511
2152
|
}
|
|
2512
2153
|
/**
|
|
2513
2154
|
* Получить элемент по индексу
|
|
2514
2155
|
* @param index - Индекс элемента
|
|
2515
|
-
* @returns Типизированный GroupNode или undefined если индекс вне границ
|
|
2156
|
+
* @returns Типизированный GroupNode proxy или undefined если индекс вне границ
|
|
2516
2157
|
*/
|
|
2517
2158
|
at(e) {
|
|
2518
|
-
|
|
2159
|
+
const t = this.items.value[e];
|
|
2160
|
+
if (t)
|
|
2161
|
+
return t.getProxy();
|
|
2519
2162
|
}
|
|
2520
2163
|
// ============================================================================
|
|
2521
2164
|
// Реализация абстрактных методов
|
|
@@ -2524,12 +2167,13 @@ class F extends A {
|
|
|
2524
2167
|
return this.items.value.map((e) => e.getValue());
|
|
2525
2168
|
}
|
|
2526
2169
|
setValue(e, t) {
|
|
2527
|
-
this.clear(), e.forEach((
|
|
2170
|
+
this.clear(), e.forEach((s) => this.push(s)), t?.emitEvent !== !1 && this.validate().catch((s) => {
|
|
2171
|
+
S.handle(s, "ArrayNode.setValue", V.LOG);
|
|
2528
2172
|
});
|
|
2529
2173
|
}
|
|
2530
2174
|
patchValue(e) {
|
|
2531
|
-
e.forEach((t,
|
|
2532
|
-
this.items.value[
|
|
2175
|
+
e.forEach((t, s) => {
|
|
2176
|
+
this.items.value[s] && t !== void 0 && this.items.value[s].patchValue(t);
|
|
2533
2177
|
});
|
|
2534
2178
|
}
|
|
2535
2179
|
/**
|
|
@@ -2550,7 +2194,7 @@ class F extends A {
|
|
|
2550
2194
|
* ```
|
|
2551
2195
|
*/
|
|
2552
2196
|
reset(e) {
|
|
2553
|
-
this.clear(), e && e.forEach((t) => this.push(t));
|
|
2197
|
+
this._arrayErrors.value = [], this.clear(), e && e.forEach((t) => this.push(t));
|
|
2554
2198
|
}
|
|
2555
2199
|
/**
|
|
2556
2200
|
* Сбросить массив к исходным значениям (initialItems)
|
|
@@ -2581,15 +2225,38 @@ class F extends A {
|
|
|
2581
2225
|
* ```
|
|
2582
2226
|
*/
|
|
2583
2227
|
resetToInitial() {
|
|
2584
|
-
this.clear(), this.initialItems.forEach((e) => this.push(e));
|
|
2228
|
+
this._arrayErrors.value = [], this.clear(), this.initialItems.forEach((e) => this.push(e));
|
|
2585
2229
|
}
|
|
2586
2230
|
async validate() {
|
|
2587
2231
|
return (await Promise.all(this.items.value.map((t) => t.validate()))).every(Boolean);
|
|
2588
2232
|
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Установить array-level validation errors
|
|
2235
|
+
*
|
|
2236
|
+
* @param errors - Массив ошибок валидации уровня массива
|
|
2237
|
+
*
|
|
2238
|
+
* @example
|
|
2239
|
+
* ```typescript
|
|
2240
|
+
* arrayNode.setErrors([{
|
|
2241
|
+
* code: 'minItems',
|
|
2242
|
+
* message: 'Минимум 1 элемент обязателен',
|
|
2243
|
+
* }]);
|
|
2244
|
+
* ```
|
|
2245
|
+
*/
|
|
2589
2246
|
setErrors(e) {
|
|
2247
|
+
this._arrayErrors.value = e;
|
|
2590
2248
|
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Очистить все errors (array-level + item-level)
|
|
2251
|
+
*
|
|
2252
|
+
* @example
|
|
2253
|
+
* ```typescript
|
|
2254
|
+
* arrayNode.clearErrors();
|
|
2255
|
+
* console.log(arrayNode.errors.value); // []
|
|
2256
|
+
* ```
|
|
2257
|
+
*/
|
|
2591
2258
|
clearErrors() {
|
|
2592
|
-
this.items.value.forEach((e) => e.clearErrors());
|
|
2259
|
+
this._arrayErrors.value = [], this.items.value.forEach((e) => e.clearErrors());
|
|
2593
2260
|
}
|
|
2594
2261
|
// ============================================================================
|
|
2595
2262
|
// Protected hooks (Template Method pattern)
|
|
@@ -2631,20 +2298,24 @@ class F extends A {
|
|
|
2631
2298
|
// ============================================================================
|
|
2632
2299
|
/**
|
|
2633
2300
|
* Итерировать по элементам массива
|
|
2634
|
-
* @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode
|
|
2301
|
+
* @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
|
|
2635
2302
|
*/
|
|
2636
2303
|
forEach(e) {
|
|
2637
|
-
this.items.value.forEach((t,
|
|
2638
|
-
|
|
2304
|
+
this.items.value.forEach((t, s) => {
|
|
2305
|
+
const i = t.getProxy();
|
|
2306
|
+
e(i, s);
|
|
2639
2307
|
});
|
|
2640
2308
|
}
|
|
2641
2309
|
/**
|
|
2642
2310
|
* Маппинг элементов массива
|
|
2643
|
-
* @param callback - Функция преобразования с типизированным GroupNode
|
|
2311
|
+
* @param callback - Функция преобразования с типизированным GroupNode proxy
|
|
2644
2312
|
* @returns Новый массив результатов
|
|
2645
2313
|
*/
|
|
2646
2314
|
map(e) {
|
|
2647
|
-
return this.items.value.map((t,
|
|
2315
|
+
return this.items.value.map((t, s) => {
|
|
2316
|
+
const i = t.getProxy();
|
|
2317
|
+
return e(i, s);
|
|
2318
|
+
});
|
|
2648
2319
|
}
|
|
2649
2320
|
// ============================================================================
|
|
2650
2321
|
// Private методы
|
|
@@ -2655,7 +2326,7 @@ class F extends A {
|
|
|
2655
2326
|
*/
|
|
2656
2327
|
createItem(e) {
|
|
2657
2328
|
if (this.isGroupSchema(this.itemSchema)) {
|
|
2658
|
-
const t = new
|
|
2329
|
+
const t = new k(this.itemSchema);
|
|
2659
2330
|
return e && t.patchValue(e), this.validationSchemaFn && "applyValidationSchema" in t && t.applyValidationSchema(this.validationSchemaFn), this.behaviorSchemaFn && "applyBehaviorSchema" in t && t.applyBehaviorSchema(this.behaviorSchemaFn), t;
|
|
2660
2331
|
}
|
|
2661
2332
|
throw new Error(
|
|
@@ -2742,14 +2413,14 @@ class F extends A {
|
|
|
2742
2413
|
* ```
|
|
2743
2414
|
*/
|
|
2744
2415
|
watchItems(e, t) {
|
|
2745
|
-
const
|
|
2746
|
-
const
|
|
2747
|
-
if (n instanceof
|
|
2416
|
+
const s = m(() => {
|
|
2417
|
+
const r = this.items.value.map((n) => {
|
|
2418
|
+
if (n instanceof k)
|
|
2748
2419
|
return n.getFieldByPath(e)?.value.value;
|
|
2749
2420
|
});
|
|
2750
|
-
t(
|
|
2751
|
-
}),
|
|
2752
|
-
return this.disposers.add(
|
|
2421
|
+
t(r);
|
|
2422
|
+
}), i = A(w.WatchItems);
|
|
2423
|
+
return this.disposers.add(i, s);
|
|
2753
2424
|
}
|
|
2754
2425
|
/**
|
|
2755
2426
|
* Подписка на изменение длины массива
|
|
@@ -2774,11 +2445,11 @@ class F extends A {
|
|
|
2774
2445
|
* ```
|
|
2775
2446
|
*/
|
|
2776
2447
|
watchLength(e) {
|
|
2777
|
-
const t =
|
|
2778
|
-
const
|
|
2779
|
-
e(
|
|
2780
|
-
}),
|
|
2781
|
-
return this.disposers.add(
|
|
2448
|
+
const t = m(() => {
|
|
2449
|
+
const i = this.length.value;
|
|
2450
|
+
e(i);
|
|
2451
|
+
}), s = A(w.WatchLength);
|
|
2452
|
+
return this.disposers.add(s, t);
|
|
2782
2453
|
}
|
|
2783
2454
|
/**
|
|
2784
2455
|
* Очистить все ресурсы узла
|
|
@@ -2841,7 +2512,7 @@ class F extends A {
|
|
|
2841
2512
|
});
|
|
2842
2513
|
}
|
|
2843
2514
|
}
|
|
2844
|
-
class
|
|
2515
|
+
class ne {
|
|
2845
2516
|
/**
|
|
2846
2517
|
* Создает узел формы на основе конфигурации
|
|
2847
2518
|
*
|
|
@@ -2891,16 +2562,16 @@ class L {
|
|
|
2891
2562
|
if (Array.isArray(e) && e.length >= 1)
|
|
2892
2563
|
return this.createArrayNodeFromArray(e);
|
|
2893
2564
|
if (this.isFieldConfig(e))
|
|
2894
|
-
return new
|
|
2565
|
+
return new ee(e);
|
|
2895
2566
|
if (this.isArrayConfig(e)) {
|
|
2896
2567
|
const t = e;
|
|
2897
|
-
return new
|
|
2568
|
+
return new L(
|
|
2898
2569
|
t.schema,
|
|
2899
2570
|
t.initialItems
|
|
2900
2571
|
);
|
|
2901
2572
|
}
|
|
2902
2573
|
if (this.isGroupConfig(e))
|
|
2903
|
-
return new
|
|
2574
|
+
return new k(e);
|
|
2904
2575
|
throw new Error(
|
|
2905
2576
|
`NodeFactory: Unknown node config. Expected FieldConfig, GroupConfig, or ArrayConfig, but got: ${JSON.stringify(
|
|
2906
2577
|
e
|
|
@@ -2934,11 +2605,11 @@ class L {
|
|
|
2934
2605
|
*/
|
|
2935
2606
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2936
2607
|
createArrayNodeFromArray(e) {
|
|
2937
|
-
const [t, ...
|
|
2938
|
-
this.isGroupConfig(t) &&
|
|
2939
|
-
for (const
|
|
2940
|
-
this.isGroupConfig(
|
|
2941
|
-
return new
|
|
2608
|
+
const [t, ...s] = e, i = [];
|
|
2609
|
+
this.isGroupConfig(t) && i.push(this.extractValues(t));
|
|
2610
|
+
for (const r of s)
|
|
2611
|
+
this.isGroupConfig(r) ? i.push(this.extractValues(r)) : i.push(r);
|
|
2612
|
+
return new L(t, i);
|
|
2942
2613
|
}
|
|
2943
2614
|
/**
|
|
2944
2615
|
* Извлечь значения из схемы (рекурсивно)
|
|
@@ -2976,8 +2647,8 @@ class L {
|
|
|
2976
2647
|
return e.map((t) => this.extractValues(t));
|
|
2977
2648
|
if (this.isGroupConfig(e)) {
|
|
2978
2649
|
const t = {};
|
|
2979
|
-
for (const [
|
|
2980
|
-
t[
|
|
2650
|
+
for (const [s, i] of Object.entries(e))
|
|
2651
|
+
t[s] = this.extractValues(i);
|
|
2981
2652
|
return t;
|
|
2982
2653
|
}
|
|
2983
2654
|
return e;
|
|
@@ -3057,287 +2728,441 @@ class L {
|
|
|
3057
2728
|
return e != null && typeof e == "object" && !this.isFieldConfig(e) && !this.isArrayConfig(e);
|
|
3058
2729
|
}
|
|
3059
2730
|
}
|
|
3060
|
-
|
|
2731
|
+
function ge(a) {
|
|
2732
|
+
return new k(a).getProxy();
|
|
2733
|
+
}
|
|
2734
|
+
class be {
|
|
3061
2735
|
/**
|
|
3062
|
-
*
|
|
2736
|
+
* @param form - Форма для наблюдения
|
|
2737
|
+
* @param options - Опции observer
|
|
2738
|
+
*/
|
|
2739
|
+
constructor(e, t) {
|
|
2740
|
+
this.form = e, this.options = {
|
|
2741
|
+
enableLogging: !1,
|
|
2742
|
+
eventTypes: ["value", "status", "errors", "touched", "dirty"],
|
|
2743
|
+
pathFilter: [],
|
|
2744
|
+
...t
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2748
|
+
disposers = [];
|
|
2749
|
+
options;
|
|
2750
|
+
/**
|
|
2751
|
+
* Подписаться на события изменения
|
|
3063
2752
|
*
|
|
3064
|
-
* @param
|
|
2753
|
+
* @param callback - Функция обработки события
|
|
2754
|
+
* @returns Функция отписки
|
|
3065
2755
|
*
|
|
3066
2756
|
* @example
|
|
3067
2757
|
* ```typescript
|
|
3068
|
-
* const
|
|
3069
|
-
*
|
|
2758
|
+
* const unsubscribe = observer.subscribe((event) => {
|
|
2759
|
+
* // Отправить событие в analytics
|
|
2760
|
+
* analytics.track('form_change', event);
|
|
2761
|
+
* });
|
|
3070
2762
|
* ```
|
|
3071
2763
|
*/
|
|
3072
|
-
|
|
3073
|
-
this.
|
|
2764
|
+
subscribe(e) {
|
|
2765
|
+
return this.listeners.add(e), () => this.listeners.delete(e);
|
|
3074
2766
|
}
|
|
3075
2767
|
/**
|
|
3076
|
-
*
|
|
3077
|
-
* @private
|
|
3078
|
-
*/
|
|
3079
|
-
timer;
|
|
3080
|
-
/**
|
|
3081
|
-
* Отложить выполнение функции
|
|
2768
|
+
* Включить трассировку формы
|
|
3082
2769
|
*
|
|
3083
|
-
*
|
|
3084
|
-
* и
|
|
2770
|
+
* Подписывается на изменения основных сигналов формы
|
|
2771
|
+
* и вызывает listeners при каждом изменении
|
|
3085
2772
|
*
|
|
3086
|
-
* @
|
|
3087
|
-
* @returns Promise, который разрешается результатом функции
|
|
2773
|
+
* @returns Функция для отключения трассировки
|
|
3088
2774
|
*
|
|
3089
2775
|
* @example
|
|
3090
2776
|
* ```typescript
|
|
3091
|
-
* const
|
|
3092
|
-
*
|
|
3093
|
-
* // Первый вызов - запланирован через 300мс
|
|
3094
|
-
* debouncer.debounce(async () => console.log('First'));
|
|
3095
|
-
*
|
|
3096
|
-
* // Второй вызов через 100мс - отменяет первый, запланирован через 300мс
|
|
3097
|
-
* debouncer.debounce(async () => console.log('Second'));
|
|
2777
|
+
* const dispose = observer.enableTracing();
|
|
3098
2778
|
*
|
|
3099
|
-
* //
|
|
2779
|
+
* // Позже, для отключения
|
|
2780
|
+
* dispose();
|
|
3100
2781
|
* ```
|
|
3101
2782
|
*/
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
}
|
|
2783
|
+
enableTracing() {
|
|
2784
|
+
if (this.shouldTrack("value")) {
|
|
2785
|
+
let e = this.form.value.value;
|
|
2786
|
+
const t = m(() => {
|
|
2787
|
+
const s = this.form.value.value;
|
|
2788
|
+
s !== e && (this.emit({
|
|
2789
|
+
type: "value",
|
|
2790
|
+
path: "",
|
|
2791
|
+
timestamp: Date.now(),
|
|
2792
|
+
oldValue: e,
|
|
2793
|
+
newValue: s
|
|
2794
|
+
}), e = s);
|
|
2795
|
+
});
|
|
2796
|
+
this.disposers.push(t);
|
|
2797
|
+
}
|
|
2798
|
+
if (this.shouldTrack("status")) {
|
|
2799
|
+
let e = this.form.status.value;
|
|
2800
|
+
const t = m(() => {
|
|
2801
|
+
const s = this.form.status.value;
|
|
2802
|
+
s !== e && (this.emit({
|
|
2803
|
+
type: "status",
|
|
2804
|
+
path: "",
|
|
2805
|
+
timestamp: Date.now(),
|
|
2806
|
+
oldValue: e,
|
|
2807
|
+
newValue: s
|
|
2808
|
+
}), e = s);
|
|
2809
|
+
});
|
|
2810
|
+
this.disposers.push(t);
|
|
2811
|
+
}
|
|
2812
|
+
if (this.shouldTrack("errors")) {
|
|
2813
|
+
let e = this.form.errors.value;
|
|
2814
|
+
const t = m(() => {
|
|
2815
|
+
const s = this.form.errors.value;
|
|
2816
|
+
s !== e && (this.emit({
|
|
2817
|
+
type: "errors",
|
|
2818
|
+
path: "",
|
|
2819
|
+
timestamp: Date.now(),
|
|
2820
|
+
oldValue: e,
|
|
2821
|
+
newValue: s
|
|
2822
|
+
}), e = s);
|
|
2823
|
+
});
|
|
2824
|
+
this.disposers.push(t);
|
|
2825
|
+
}
|
|
2826
|
+
if (this.shouldTrack("touched")) {
|
|
2827
|
+
let e = this.form.touched.value;
|
|
2828
|
+
const t = m(() => {
|
|
2829
|
+
const s = this.form.touched.value;
|
|
2830
|
+
s !== e && (this.emit({
|
|
2831
|
+
type: "touched",
|
|
2832
|
+
path: "",
|
|
2833
|
+
timestamp: Date.now(),
|
|
2834
|
+
oldValue: e,
|
|
2835
|
+
newValue: s
|
|
2836
|
+
}), e = s);
|
|
2837
|
+
});
|
|
2838
|
+
this.disposers.push(t);
|
|
2839
|
+
}
|
|
2840
|
+
if (this.shouldTrack("dirty")) {
|
|
2841
|
+
let e = this.form.dirty.value;
|
|
2842
|
+
const t = m(() => {
|
|
2843
|
+
const s = this.form.dirty.value;
|
|
2844
|
+
s !== e && (this.emit({
|
|
2845
|
+
type: "dirty",
|
|
2846
|
+
path: "",
|
|
2847
|
+
timestamp: Date.now(),
|
|
2848
|
+
oldValue: e,
|
|
2849
|
+
newValue: s
|
|
2850
|
+
}), e = s);
|
|
2851
|
+
});
|
|
2852
|
+
this.disposers.push(t);
|
|
2853
|
+
}
|
|
2854
|
+
return () => {
|
|
2855
|
+
this.disposers.forEach((e) => e()), this.disposers = [];
|
|
2856
|
+
};
|
|
3117
2857
|
}
|
|
3118
2858
|
/**
|
|
3119
|
-
*
|
|
2859
|
+
* Наблюдать за конкретным полем
|
|
3120
2860
|
*
|
|
3121
|
-
*
|
|
3122
|
-
*
|
|
2861
|
+
* @param path - Путь к полю
|
|
2862
|
+
* @returns Функция для отключения наблюдения
|
|
3123
2863
|
*
|
|
3124
2864
|
* @example
|
|
3125
2865
|
* ```typescript
|
|
3126
|
-
*
|
|
3127
|
-
*
|
|
3128
|
-
* debouncer.debounce(async () => {
|
|
3129
|
-
* console.log('This will not execute');
|
|
3130
|
-
* });
|
|
3131
|
-
*
|
|
3132
|
-
* debouncer.cancel(); // Отменяем вызов
|
|
2866
|
+
* // Наблюдать за полем email
|
|
2867
|
+
* const dispose = observer.watchField('email');
|
|
3133
2868
|
* ```
|
|
3134
2869
|
*/
|
|
3135
|
-
|
|
3136
|
-
|
|
2870
|
+
watchField(e) {
|
|
2871
|
+
const t = this.form.getFieldByPath(e);
|
|
2872
|
+
if (!t)
|
|
2873
|
+
return () => {
|
|
2874
|
+
};
|
|
2875
|
+
const s = [];
|
|
2876
|
+
if (this.shouldTrack("value")) {
|
|
2877
|
+
let i = t.value.value;
|
|
2878
|
+
const r = m(() => {
|
|
2879
|
+
const n = t.value.value;
|
|
2880
|
+
n !== i && (this.emit({
|
|
2881
|
+
type: "value",
|
|
2882
|
+
path: e,
|
|
2883
|
+
timestamp: Date.now(),
|
|
2884
|
+
oldValue: i,
|
|
2885
|
+
newValue: n
|
|
2886
|
+
}), i = n);
|
|
2887
|
+
});
|
|
2888
|
+
s.push(r);
|
|
2889
|
+
}
|
|
2890
|
+
if (this.shouldTrack("status")) {
|
|
2891
|
+
let i = t.status.value;
|
|
2892
|
+
const r = m(() => {
|
|
2893
|
+
const n = t.status.value;
|
|
2894
|
+
n !== i && (this.emit({
|
|
2895
|
+
type: "status",
|
|
2896
|
+
path: e,
|
|
2897
|
+
timestamp: Date.now(),
|
|
2898
|
+
oldValue: i,
|
|
2899
|
+
newValue: n
|
|
2900
|
+
}), i = n);
|
|
2901
|
+
});
|
|
2902
|
+
s.push(r);
|
|
2903
|
+
}
|
|
2904
|
+
return () => s.forEach((i) => i());
|
|
3137
2905
|
}
|
|
3138
2906
|
/**
|
|
3139
|
-
*
|
|
3140
|
-
*
|
|
3141
|
-
* Полезно когда нужно принудительно выполнить действие сейчас,
|
|
3142
|
-
* игнорируя debounce.
|
|
3143
|
-
*
|
|
3144
|
-
* @param fn Функция для немедленного выполнения
|
|
3145
|
-
* @returns Promise с результатом функции
|
|
3146
|
-
*
|
|
3147
|
-
* @example
|
|
3148
|
-
* ```typescript
|
|
3149
|
-
* const debouncer = new Debouncer(300);
|
|
3150
|
-
*
|
|
3151
|
-
* // Запланировано через 300мс
|
|
3152
|
-
* debouncer.debounce(async () => console.log('Delayed'));
|
|
3153
|
-
*
|
|
3154
|
-
* // Отменяем отложенный и выполняем немедленно
|
|
3155
|
-
* await debouncer.flush(async () => console.log('Immediate'));
|
|
3156
|
-
* // Выведет: "Immediate" (сразу)
|
|
3157
|
-
* // "Delayed" не выполнится (отменен)
|
|
3158
|
-
* ```
|
|
2907
|
+
* Отправить событие всем listeners
|
|
3159
2908
|
*/
|
|
3160
|
-
|
|
3161
|
-
|
|
2909
|
+
emit(e) {
|
|
2910
|
+
this.matchesPathFilter(e.path) && (this.options.enableLogging && console.log(`[FormObserver] ${e.type} at "${e.path || "root"}":`, e.newValue), this.listeners.forEach((t) => {
|
|
2911
|
+
try {
|
|
2912
|
+
t(e);
|
|
2913
|
+
} catch {
|
|
2914
|
+
}
|
|
2915
|
+
}));
|
|
3162
2916
|
}
|
|
3163
2917
|
/**
|
|
3164
|
-
* Проверить,
|
|
3165
|
-
*
|
|
3166
|
-
* @returns true если есть запланированный вызов
|
|
3167
|
-
*
|
|
3168
|
-
* @example
|
|
3169
|
-
* ```typescript
|
|
3170
|
-
* const debouncer = new Debouncer(300);
|
|
3171
|
-
*
|
|
3172
|
-
* console.log(debouncer.isPending()); // false
|
|
3173
|
-
*
|
|
3174
|
-
* debouncer.debounce(() => console.log('Test'));
|
|
3175
|
-
* console.log(debouncer.isPending()); // true
|
|
3176
|
-
*
|
|
3177
|
-
* // Через 300мс
|
|
3178
|
-
* console.log(debouncer.isPending()); // false
|
|
3179
|
-
* ```
|
|
2918
|
+
* Проверить, нужно ли отслеживать тип события
|
|
3180
2919
|
*/
|
|
3181
|
-
|
|
3182
|
-
return this.
|
|
2920
|
+
shouldTrack(e) {
|
|
2921
|
+
return this.options.eventTypes.includes(e);
|
|
2922
|
+
}
|
|
2923
|
+
/**
|
|
2924
|
+
* Проверить, соответствует ли путь фильтру
|
|
2925
|
+
*/
|
|
2926
|
+
matchesPathFilter(e) {
|
|
2927
|
+
const { pathFilter: t } = this.options;
|
|
2928
|
+
return !t || Array.isArray(t) && t.length === 0 ? !0 : t instanceof RegExp ? t.test(e) : t.includes(e);
|
|
2929
|
+
}
|
|
2930
|
+
/**
|
|
2931
|
+
* Очистить все подписки и disposers
|
|
2932
|
+
*/
|
|
2933
|
+
dispose() {
|
|
2934
|
+
this.disposers.forEach((e) => e()), this.disposers = [], this.listeners.clear();
|
|
3183
2935
|
}
|
|
3184
2936
|
}
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
2937
|
+
var x = { exports: {} }, T = {};
|
|
2938
|
+
var B;
|
|
2939
|
+
function oe() {
|
|
2940
|
+
if (B) return T;
|
|
2941
|
+
B = 1;
|
|
2942
|
+
var a = U;
|
|
2943
|
+
function e(h, f) {
|
|
2944
|
+
return h === f && (h !== 0 || 1 / h === 1 / f) || h !== h && f !== f;
|
|
2945
|
+
}
|
|
2946
|
+
var t = typeof Object.is == "function" ? Object.is : e, s = a.useState, i = a.useEffect, r = a.useLayoutEffect, n = a.useDebugValue;
|
|
2947
|
+
function o(h, f) {
|
|
2948
|
+
var c = f(), p = s({ inst: { value: c, getSnapshot: f } }), y = p[0].inst, E = p[1];
|
|
2949
|
+
return r(
|
|
2950
|
+
function() {
|
|
2951
|
+
y.value = c, y.getSnapshot = f, l(y) && E({ inst: y });
|
|
2952
|
+
},
|
|
2953
|
+
[h, c, f]
|
|
2954
|
+
), i(
|
|
2955
|
+
function() {
|
|
2956
|
+
return l(y) && E({ inst: y }), h(function() {
|
|
2957
|
+
l(y) && E({ inst: y });
|
|
2958
|
+
});
|
|
2959
|
+
},
|
|
2960
|
+
[h]
|
|
2961
|
+
), n(c), c;
|
|
2962
|
+
}
|
|
2963
|
+
function l(h) {
|
|
2964
|
+
var f = h.getSnapshot;
|
|
2965
|
+
h = h.value;
|
|
2966
|
+
try {
|
|
2967
|
+
var c = f();
|
|
2968
|
+
return !t(h, c);
|
|
2969
|
+
} catch {
|
|
2970
|
+
return !0;
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
function u(h, f) {
|
|
2974
|
+
return f();
|
|
2975
|
+
}
|
|
2976
|
+
var v = typeof window > "u" || typeof window.document > "u" || typeof window.document.createElement > "u" ? u : o;
|
|
2977
|
+
return T.useSyncExternalStore = a.useSyncExternalStore !== void 0 ? a.useSyncExternalStore : v, T;
|
|
3196
2978
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
2979
|
+
var O = {};
|
|
2980
|
+
var G;
|
|
2981
|
+
function le() {
|
|
2982
|
+
return G || (G = 1, process.env.NODE_ENV !== "production" && (function() {
|
|
2983
|
+
function a(c, p) {
|
|
2984
|
+
return c === p && (c !== 0 || 1 / c === 1 / p) || c !== c && p !== p;
|
|
2985
|
+
}
|
|
2986
|
+
function e(c, p) {
|
|
2987
|
+
v || i.startTransition === void 0 || (v = !0, console.error(
|
|
2988
|
+
"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
|
|
2989
|
+
));
|
|
2990
|
+
var y = p();
|
|
2991
|
+
if (!h) {
|
|
2992
|
+
var E = p();
|
|
2993
|
+
r(y, E) || (console.error(
|
|
2994
|
+
"The result of getSnapshot should be cached to avoid an infinite loop"
|
|
2995
|
+
), h = !0);
|
|
3208
2996
|
}
|
|
3209
|
-
|
|
2997
|
+
E = n({
|
|
2998
|
+
inst: { value: y, getSnapshot: p }
|
|
2999
|
+
});
|
|
3000
|
+
var _ = E[0].inst, F = E[1];
|
|
3001
|
+
return l(
|
|
3002
|
+
function() {
|
|
3003
|
+
_.value = y, _.getSnapshot = p, t(_) && F({ inst: _ });
|
|
3004
|
+
},
|
|
3005
|
+
[c, y, p]
|
|
3006
|
+
), o(
|
|
3007
|
+
function() {
|
|
3008
|
+
return t(_) && F({ inst: _ }), c(function() {
|
|
3009
|
+
t(_) && F({ inst: _ });
|
|
3010
|
+
});
|
|
3011
|
+
},
|
|
3012
|
+
[c]
|
|
3013
|
+
), u(y), y;
|
|
3210
3014
|
}
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
totalCount: t.length
|
|
3221
|
-
// Можно расширить для поддержки серверной пагинации
|
|
3222
|
-
};
|
|
3015
|
+
function t(c) {
|
|
3016
|
+
var p = c.getSnapshot;
|
|
3017
|
+
c = c.value;
|
|
3018
|
+
try {
|
|
3019
|
+
var y = p();
|
|
3020
|
+
return !r(c, y);
|
|
3021
|
+
} catch {
|
|
3022
|
+
return !0;
|
|
3023
|
+
}
|
|
3223
3024
|
}
|
|
3224
|
-
|
|
3025
|
+
function s(c, p) {
|
|
3026
|
+
return p();
|
|
3027
|
+
}
|
|
3028
|
+
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ < "u" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart == "function" && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
3029
|
+
var i = U, r = typeof Object.is == "function" ? Object.is : a, n = i.useState, o = i.useEffect, l = i.useLayoutEffect, u = i.useDebugValue, v = !1, h = !1, f = typeof window > "u" || typeof window.document > "u" || typeof window.document.createElement > "u" ? s : e;
|
|
3030
|
+
O.useSyncExternalStore = i.useSyncExternalStore !== void 0 ? i.useSyncExternalStore : f, typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ < "u" && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop == "function" && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
3031
|
+
})()), O;
|
|
3225
3032
|
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
function
|
|
3232
|
-
if (
|
|
3233
|
-
if (
|
|
3234
|
-
for (let t = 0; t <
|
|
3235
|
-
if (
|
|
3033
|
+
var $;
|
|
3034
|
+
function ue() {
|
|
3035
|
+
return $ || ($ = 1, process.env.NODE_ENV === "production" ? x.exports = oe() : x.exports = le()), x.exports;
|
|
3036
|
+
}
|
|
3037
|
+
var R = ue();
|
|
3038
|
+
function he(a, e) {
|
|
3039
|
+
if (a === e) return !0;
|
|
3040
|
+
if (a.length !== e.length) return !1;
|
|
3041
|
+
for (let t = 0; t < a.length; t++)
|
|
3042
|
+
if (a[t] !== e[t]) return !1;
|
|
3236
3043
|
return !0;
|
|
3237
3044
|
}
|
|
3238
|
-
function
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
if (r.value.value, r.disabled.value, r.errors.value, r.pending.value, r.valid.value, r.invalid.value, r.touched.value, r.shouldShowError.value, r.componentProps.value, a) {
|
|
3255
|
-
a = !1;
|
|
3045
|
+
function q(a, e, t) {
|
|
3046
|
+
const s = P(null);
|
|
3047
|
+
if (s.current === null) {
|
|
3048
|
+
const n = {};
|
|
3049
|
+
for (const o in a)
|
|
3050
|
+
n[o] = a[o].value;
|
|
3051
|
+
s.current = { ...n, __snapshot: null };
|
|
3052
|
+
}
|
|
3053
|
+
const i = b(
|
|
3054
|
+
(n) => {
|
|
3055
|
+
let o = !0;
|
|
3056
|
+
return m(() => {
|
|
3057
|
+
for (const u in a)
|
|
3058
|
+
a[u].value;
|
|
3059
|
+
if (o) {
|
|
3060
|
+
o = !1;
|
|
3256
3061
|
return;
|
|
3257
3062
|
}
|
|
3258
|
-
|
|
3063
|
+
n();
|
|
3259
3064
|
});
|
|
3260
3065
|
},
|
|
3261
|
-
[
|
|
3262
|
-
),
|
|
3263
|
-
const
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
componentProps: o
|
|
3274
|
-
}), s.snapshot;
|
|
3275
|
-
}, [r]);
|
|
3276
|
-
return w(t, i, i);
|
|
3277
|
-
}
|
|
3278
|
-
function H(r) {
|
|
3279
|
-
const e = V({
|
|
3280
|
-
snapshot: null,
|
|
3281
|
-
value: r.value.value,
|
|
3282
|
-
length: r.length.value,
|
|
3283
|
-
errors: r.errors.value,
|
|
3284
|
-
pending: r.pending.value,
|
|
3285
|
-
valid: r.valid.value,
|
|
3286
|
-
invalid: r.invalid.value,
|
|
3287
|
-
touched: r.touched.value,
|
|
3288
|
-
dirty: r.dirty.value
|
|
3289
|
-
}), t = y(
|
|
3290
|
-
(s) => {
|
|
3291
|
-
let a = !0;
|
|
3292
|
-
return v(() => {
|
|
3293
|
-
if (r.value.value, r.length.value, r.errors.value, r.pending.value, r.valid.value, r.invalid.value, r.touched.value, r.dirty.value, a) {
|
|
3294
|
-
a = !1;
|
|
3295
|
-
return;
|
|
3066
|
+
[a]
|
|
3067
|
+
), r = b(() => {
|
|
3068
|
+
const n = s.current, o = {};
|
|
3069
|
+
for (const u in a)
|
|
3070
|
+
o[u] = a[u].value;
|
|
3071
|
+
let l = !1;
|
|
3072
|
+
for (const u of e) {
|
|
3073
|
+
const { key: v, useShallowArrayEqual: h } = u, f = o[v], c = n[v];
|
|
3074
|
+
if (h) {
|
|
3075
|
+
if (!he(c, f)) {
|
|
3076
|
+
l = !0;
|
|
3077
|
+
break;
|
|
3296
3078
|
}
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
invalid: c,
|
|
3310
|
-
touched: f,
|
|
3311
|
-
dirty: p
|
|
3312
|
-
}), s.snapshot;
|
|
3313
|
-
}, [r]);
|
|
3314
|
-
return w(t, i, i);
|
|
3079
|
+
} else if (c !== f) {
|
|
3080
|
+
l = !0;
|
|
3081
|
+
break;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
if (!l && n.__snapshot)
|
|
3085
|
+
return n.__snapshot;
|
|
3086
|
+
for (const u in a)
|
|
3087
|
+
n[u] = o[u];
|
|
3088
|
+
return n.__snapshot = t(o), n.__snapshot;
|
|
3089
|
+
}, [a, t]);
|
|
3090
|
+
return R.useSyncExternalStore(i, r, r);
|
|
3315
3091
|
}
|
|
3316
|
-
function
|
|
3317
|
-
const e =
|
|
3318
|
-
value:
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
},
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3092
|
+
function de(a) {
|
|
3093
|
+
const e = {
|
|
3094
|
+
value: a.value,
|
|
3095
|
+
disabled: a.disabled,
|
|
3096
|
+
errors: a.errors,
|
|
3097
|
+
pending: a.pending,
|
|
3098
|
+
valid: a.valid,
|
|
3099
|
+
invalid: a.invalid,
|
|
3100
|
+
touched: a.touched,
|
|
3101
|
+
shouldShowError: a.shouldShowError,
|
|
3102
|
+
componentProps: a.componentProps
|
|
3103
|
+
}, t = [
|
|
3104
|
+
{ key: "value" },
|
|
3105
|
+
{ key: "disabled" },
|
|
3106
|
+
{ key: "errors", useShallowArrayEqual: !0 },
|
|
3107
|
+
{ key: "pending" },
|
|
3108
|
+
{ key: "valid" },
|
|
3109
|
+
{ key: "invalid" },
|
|
3110
|
+
{ key: "touched" },
|
|
3111
|
+
{ key: "shouldShowError" },
|
|
3112
|
+
{ key: "componentProps" }
|
|
3113
|
+
], s = b(
|
|
3114
|
+
(i) => ({
|
|
3115
|
+
value: i.value,
|
|
3116
|
+
pending: i.pending,
|
|
3117
|
+
disabled: i.disabled,
|
|
3118
|
+
errors: i.errors,
|
|
3119
|
+
valid: i.valid,
|
|
3120
|
+
invalid: i.invalid,
|
|
3121
|
+
touched: i.touched,
|
|
3122
|
+
shouldShowError: i.shouldShowError,
|
|
3123
|
+
componentProps: i.componentProps
|
|
3124
|
+
}),
|
|
3125
|
+
[]
|
|
3126
|
+
);
|
|
3127
|
+
return q(e, t, s);
|
|
3128
|
+
}
|
|
3129
|
+
function ce(a) {
|
|
3130
|
+
const e = {
|
|
3131
|
+
value: a.value,
|
|
3132
|
+
length: a.length,
|
|
3133
|
+
errors: a.errors,
|
|
3134
|
+
pending: a.pending,
|
|
3135
|
+
valid: a.valid,
|
|
3136
|
+
invalid: a.invalid,
|
|
3137
|
+
touched: a.touched,
|
|
3138
|
+
dirty: a.dirty
|
|
3139
|
+
}, t = [
|
|
3140
|
+
{ key: "value" },
|
|
3141
|
+
{ key: "length" },
|
|
3142
|
+
{ key: "errors", useShallowArrayEqual: !0 },
|
|
3143
|
+
{ key: "pending" },
|
|
3144
|
+
{ key: "valid" },
|
|
3145
|
+
{ key: "invalid" },
|
|
3146
|
+
{ key: "touched" },
|
|
3147
|
+
{ key: "dirty" }
|
|
3148
|
+
], s = b(
|
|
3149
|
+
(i) => ({
|
|
3150
|
+
value: i.value,
|
|
3151
|
+
length: i.length,
|
|
3152
|
+
pending: i.pending,
|
|
3153
|
+
errors: i.errors,
|
|
3154
|
+
valid: i.valid,
|
|
3155
|
+
invalid: i.invalid,
|
|
3156
|
+
touched: i.touched,
|
|
3157
|
+
dirty: i.dirty
|
|
3158
|
+
}),
|
|
3159
|
+
[]
|
|
3160
|
+
);
|
|
3161
|
+
return q(e, t, s);
|
|
3337
3162
|
}
|
|
3338
|
-
function
|
|
3339
|
-
const e =
|
|
3340
|
-
return
|
|
3163
|
+
function Ee(a) {
|
|
3164
|
+
const e = a && "length" in a && "map" in a;
|
|
3165
|
+
return a ? e ? ce(a) : de(a) : {
|
|
3341
3166
|
value: [],
|
|
3342
3167
|
length: 0,
|
|
3343
3168
|
pending: !1,
|
|
@@ -3348,33 +3173,99 @@ function re(r) {
|
|
|
3348
3173
|
dirty: !1
|
|
3349
3174
|
};
|
|
3350
3175
|
}
|
|
3176
|
+
function _e(a) {
|
|
3177
|
+
const e = P({ value: a.value.value }), t = b(
|
|
3178
|
+
(i) => {
|
|
3179
|
+
let r = !0;
|
|
3180
|
+
return m(() => {
|
|
3181
|
+
if (a.value.value, r) {
|
|
3182
|
+
r = !1;
|
|
3183
|
+
return;
|
|
3184
|
+
}
|
|
3185
|
+
i();
|
|
3186
|
+
});
|
|
3187
|
+
},
|
|
3188
|
+
[a]
|
|
3189
|
+
), s = b(() => {
|
|
3190
|
+
const i = a.value.value;
|
|
3191
|
+
return e.current.value === i ? e.current.value : (e.current.value = i, i);
|
|
3192
|
+
}, [a]);
|
|
3193
|
+
return R.useSyncExternalStore(t, s, s);
|
|
3194
|
+
}
|
|
3195
|
+
function Se(a) {
|
|
3196
|
+
const e = P({ length: a.length.value }), t = b(
|
|
3197
|
+
(i) => {
|
|
3198
|
+
let r = !0;
|
|
3199
|
+
return m(() => {
|
|
3200
|
+
if (a.length.value, r) {
|
|
3201
|
+
r = !1;
|
|
3202
|
+
return;
|
|
3203
|
+
}
|
|
3204
|
+
i();
|
|
3205
|
+
});
|
|
3206
|
+
},
|
|
3207
|
+
[a]
|
|
3208
|
+
), s = b(() => {
|
|
3209
|
+
const i = a.length.value;
|
|
3210
|
+
return e.current.length === i ? e.current.length : (e.current.length = i, i);
|
|
3211
|
+
}, [a]);
|
|
3212
|
+
return R.useSyncExternalStore(t, s, s);
|
|
3213
|
+
}
|
|
3214
|
+
function Ve(a, e, t) {
|
|
3215
|
+
const s = b(
|
|
3216
|
+
(r) => {
|
|
3217
|
+
const n = e, o = [];
|
|
3218
|
+
for (const l of Object.keys(n)) {
|
|
3219
|
+
const u = n[l];
|
|
3220
|
+
if (u && typeof u == "object" && u.value && typeof u.value.subscribe == "function") {
|
|
3221
|
+
const v = u.value.subscribe(r);
|
|
3222
|
+
o.push(v);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
return () => {
|
|
3226
|
+
o.forEach((l) => l());
|
|
3227
|
+
};
|
|
3228
|
+
},
|
|
3229
|
+
[e]
|
|
3230
|
+
), i = b(() => a ? a(e, t) : !1, [a, e, t]);
|
|
3231
|
+
return Q(s, i, i);
|
|
3232
|
+
}
|
|
3351
3233
|
export {
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3234
|
+
ke as AbstractRegistry,
|
|
3235
|
+
L as ArrayNode,
|
|
3236
|
+
V as ErrorStrategy,
|
|
3237
|
+
ee as FieldNode,
|
|
3238
|
+
se as FieldPathNavigator,
|
|
3239
|
+
S as FormErrorHandler,
|
|
3240
|
+
C as FormNode,
|
|
3241
|
+
be as FormObserver,
|
|
3242
|
+
Z as FormStatusMachine,
|
|
3243
|
+
ae as FormSubmitter,
|
|
3244
|
+
k as GroupNode,
|
|
3245
|
+
ne as NodeFactory,
|
|
3246
|
+
xe as RegistryStack,
|
|
3247
|
+
D as SubscriptionManager,
|
|
3248
|
+
$e as behaviors,
|
|
3249
|
+
I as createFieldPath,
|
|
3250
|
+
ge as createForm,
|
|
3251
|
+
Ie as extractKey,
|
|
3252
|
+
Le as extractPath,
|
|
3253
|
+
Fe as getCurrentBehaviorRegistry,
|
|
3254
|
+
Te as getCurrentValidationRegistry,
|
|
3255
|
+
Pe as getNodeType,
|
|
3256
|
+
N as isArrayNode,
|
|
3257
|
+
M as isFieldNode,
|
|
3258
|
+
Ce as isFormNode,
|
|
3259
|
+
De as isGroupNode,
|
|
3260
|
+
We as runOutsideEffect,
|
|
3261
|
+
qe as safeCallback,
|
|
3262
|
+
je as safeDebouncedCallback,
|
|
3263
|
+
Be as toFieldPath,
|
|
3264
|
+
A as uniqueId,
|
|
3265
|
+
Se as useArrayLength,
|
|
3266
|
+
Ee as useFormControl,
|
|
3267
|
+
_e as useFormControlValue,
|
|
3268
|
+
Ve as useHiddenCondition,
|
|
3269
|
+
Re as validateForm,
|
|
3270
|
+
Me as validators
|
|
3380
3271
|
};
|