@reformer/core 1.1.0 → 2.0.0-beta.3

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.
Files changed (99) hide show
  1. package/dist/behaviors-DzYL8kY_.js +499 -0
  2. package/dist/behaviors.d.ts +6 -2
  3. package/dist/behaviors.js +19 -227
  4. package/dist/core/behavior/behavior-context.d.ts +6 -2
  5. package/dist/core/behavior/create-field-path.d.ts +3 -16
  6. package/dist/core/nodes/group-node.d.ts +14 -193
  7. package/dist/core/types/form-context.d.ts +10 -4
  8. package/dist/core/utils/field-path.d.ts +48 -0
  9. package/dist/core/utils/index.d.ts +1 -0
  10. package/dist/core/validation/core/validate-tree.d.ts +10 -4
  11. package/dist/core/validation/field-path.d.ts +3 -39
  12. package/dist/core/validation/validation-context.d.ts +23 -0
  13. package/dist/hooks/types.d.ts +328 -0
  14. package/dist/hooks/useFormControl.d.ts +13 -37
  15. package/dist/hooks/useFormControlValue.d.ts +167 -0
  16. package/dist/hooks/useSignalSubscription.d.ts +17 -0
  17. package/dist/index.d.ts +6 -1
  18. package/dist/index.js +2886 -8
  19. package/dist/{create-field-path-CdPF3lIK.js → registry-helpers-BRxAr6nG.js} +133 -347
  20. package/dist/validators-gXoHPdqM.js +418 -0
  21. package/dist/validators.d.ts +6 -2
  22. package/dist/validators.js +29 -296
  23. package/llms.txt +1283 -22
  24. package/package.json +8 -4
  25. package/dist/core/behavior/behavior-applicator.d.ts +0 -71
  26. package/dist/core/behavior/behavior-applicator.js +0 -92
  27. package/dist/core/behavior/behavior-context.js +0 -38
  28. package/dist/core/behavior/behavior-registry.js +0 -198
  29. package/dist/core/behavior/behaviors/compute-from.js +0 -84
  30. package/dist/core/behavior/behaviors/copy-from.js +0 -64
  31. package/dist/core/behavior/behaviors/enable-when.js +0 -81
  32. package/dist/core/behavior/behaviors/index.js +0 -11
  33. package/dist/core/behavior/behaviors/reset-when.js +0 -63
  34. package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
  35. package/dist/core/behavior/behaviors/sync-fields.js +0 -66
  36. package/dist/core/behavior/behaviors/transform-value.js +0 -110
  37. package/dist/core/behavior/behaviors/watch-field.js +0 -56
  38. package/dist/core/behavior/compose-behavior.js +0 -166
  39. package/dist/core/behavior/create-field-path.js +0 -69
  40. package/dist/core/behavior/index.js +0 -17
  41. package/dist/core/behavior/types.js +0 -7
  42. package/dist/core/context/form-context-impl.js +0 -37
  43. package/dist/core/factories/index.js +0 -6
  44. package/dist/core/factories/node-factory.js +0 -281
  45. package/dist/core/nodes/array-node.js +0 -534
  46. package/dist/core/nodes/field-node.js +0 -510
  47. package/dist/core/nodes/form-node.js +0 -343
  48. package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
  49. package/dist/core/nodes/group-node/field-registry.js +0 -215
  50. package/dist/core/nodes/group-node/index.d.ts +0 -11
  51. package/dist/core/nodes/group-node/index.js +0 -11
  52. package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
  53. package/dist/core/nodes/group-node/proxy-builder.js +0 -161
  54. package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
  55. package/dist/core/nodes/group-node/state-manager.js +0 -265
  56. package/dist/core/nodes/group-node.js +0 -770
  57. package/dist/core/types/deep-schema.js +0 -11
  58. package/dist/core/types/field-path.js +0 -4
  59. package/dist/core/types/form-context.js +0 -25
  60. package/dist/core/types/group-node-proxy.js +0 -31
  61. package/dist/core/types/index.js +0 -4
  62. package/dist/core/types/validation-schema.js +0 -10
  63. package/dist/core/utils/create-form.js +0 -24
  64. package/dist/core/utils/debounce.js +0 -197
  65. package/dist/core/utils/error-handler.js +0 -226
  66. package/dist/core/utils/field-path-navigator.js +0 -374
  67. package/dist/core/utils/index.js +0 -14
  68. package/dist/core/utils/registry-helpers.js +0 -79
  69. package/dist/core/utils/registry-stack.js +0 -86
  70. package/dist/core/utils/resources.js +0 -69
  71. package/dist/core/utils/subscription-manager.js +0 -214
  72. package/dist/core/utils/type-guards.js +0 -169
  73. package/dist/core/validation/core/apply-when.js +0 -41
  74. package/dist/core/validation/core/apply.js +0 -38
  75. package/dist/core/validation/core/index.js +0 -8
  76. package/dist/core/validation/core/validate-async.js +0 -45
  77. package/dist/core/validation/core/validate-tree.js +0 -37
  78. package/dist/core/validation/core/validate.js +0 -38
  79. package/dist/core/validation/field-path.js +0 -147
  80. package/dist/core/validation/index.js +0 -33
  81. package/dist/core/validation/validate-form.js +0 -152
  82. package/dist/core/validation/validation-applicator.js +0 -217
  83. package/dist/core/validation/validation-context.js +0 -75
  84. package/dist/core/validation/validation-registry.js +0 -298
  85. package/dist/core/validation/validators/array-validators.js +0 -86
  86. package/dist/core/validation/validators/date.js +0 -117
  87. package/dist/core/validation/validators/email.js +0 -60
  88. package/dist/core/validation/validators/index.js +0 -14
  89. package/dist/core/validation/validators/max-length.js +0 -60
  90. package/dist/core/validation/validators/max.js +0 -60
  91. package/dist/core/validation/validators/min-length.js +0 -60
  92. package/dist/core/validation/validators/min.js +0 -60
  93. package/dist/core/validation/validators/number.js +0 -90
  94. package/dist/core/validation/validators/pattern.js +0 -62
  95. package/dist/core/validation/validators/phone.js +0 -58
  96. package/dist/core/validation/validators/required.js +0 -69
  97. package/dist/core/validation/validators/url.js +0 -55
  98. package/dist/hooks/useFormControl.js +0 -298
  99. package/dist/node-factory-D7DOnSSN.js +0 -3200
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reformer/core",
3
- "version": "1.1.0",
3
+ "version": "2.0.0-beta.3",
4
4
  "description": "Reactive form state management library for React with signals-based architecture",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,8 @@
22
22
  },
23
23
  "sideEffects": false,
24
24
  "scripts": {
25
- "build": "vite build && tsc -p tsconfig.json",
25
+ "generate:llms": "npx tsx scripts/generate-llms.ts",
26
+ "build": "npm run generate:llms && vite build && tsc -p tsconfig.json",
26
27
  "dev": "vite",
27
28
  "test": "vitest",
28
29
  "test:watch": "vitest watch"
@@ -63,15 +64,18 @@
63
64
  "LLMs.txt"
64
65
  ],
65
66
  "peerDependencies": {
66
- "react": "^18.0.0 || ^19.0.0",
67
- "react-dom": "^18.0.0 || ^19.0.0"
67
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
68
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
68
69
  },
69
70
  "dependencies": {
70
71
  "@preact/signals-core": "^1.8.0",
72
+ "use-sync-external-store": "^1.2.0",
71
73
  "uuid": "^13.0.0"
72
74
  },
73
75
  "devDependencies": {
76
+ "@types/use-sync-external-store": "^0.0.6",
74
77
  "@types/node": "^24.10.1",
78
+ "tsx": "^4.19.2",
75
79
  "@types/react": "^19.2.7",
76
80
  "@types/react-dom": "^19.2.3",
77
81
  "@types/uuid": "^10.0.0",
@@ -1,71 +0,0 @@
1
- /**
2
- * Применение behavior схемы к форме
3
- *
4
- * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
- * Управляет процессом регистрации и применения behaviors.
6
- *
7
- * @template T Тип формы
8
- *
9
- * @example
10
- * ```typescript
11
- * class GroupNode {
12
- * private readonly behaviorApplicator = new BehaviorApplicator(this);
13
- *
14
- * applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void {
15
- * return this.behaviorApplicator.apply(schemaFn);
16
- * }
17
- * }
18
- * ```
19
- */
20
- import type { GroupNode } from '../nodes/group-node';
21
- import type { BehaviorSchemaFn } from './types';
22
- import { BehaviorRegistry } from './behavior-registry';
23
- /**
24
- * Класс для применения behavior схемы к форме
25
- *
26
- * Выполняет:
27
- * 1. Начало регистрации behaviors (beginRegistration)
28
- * 2. Выполнение схемы (регистрация behaviors)
29
- * 3. Завершение регистрации (endRegistration) - применение behaviors
30
- * 4. Возврат функции cleanup для отписки
31
- *
32
- * @template T Тип формы (объект)
33
- */
34
- export declare class BehaviorApplicator<T> {
35
- private readonly form;
36
- private readonly behaviorRegistry;
37
- constructor(form: GroupNode<T>, behaviorRegistry: BehaviorRegistry);
38
- /**
39
- * Применить behavior схему к форме
40
- *
41
- * Этапы:
42
- * 1. Начать регистрацию (beginRegistration)
43
- * 2. Выполнить схему (регистрация behaviors)
44
- * 3. Завершить регистрацию (endRegistration) - применить behaviors
45
- * 4. Вернуть функцию cleanup для отписки
46
- *
47
- * @param schemaFn Функция-схема behavior
48
- * @returns Функция отписки от всех behaviors
49
- *
50
- * @example
51
- * ```typescript
52
- * const cleanup = behaviorApplicator.apply((path) => {
53
- * copyFrom(path.residenceAddress, path.registrationAddress, {
54
- * when: (form) => form.sameAsRegistration === true
55
- * });
56
- *
57
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
58
- *
59
- * computeFrom(
60
- * path.initialPayment,
61
- * [path.propertyValue],
62
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
63
- * );
64
- * });
65
- *
66
- * // Cleanup при unmount
67
- * useEffect(() => cleanup, []);
68
- * ```
69
- */
70
- apply(schemaFn: BehaviorSchemaFn<T>): () => void;
71
- }
@@ -1,92 +0,0 @@
1
- /**
2
- * Применение behavior схемы к форме
3
- *
4
- * Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
5
- * Управляет процессом регистрации и применения behaviors.
6
- *
7
- * @template T Тип формы
8
- *
9
- * @example
10
- * ```typescript
11
- * class GroupNode {
12
- * private readonly behaviorApplicator = new BehaviorApplicator(this);
13
- *
14
- * applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void {
15
- * return this.behaviorApplicator.apply(schemaFn);
16
- * }
17
- * }
18
- * ```
19
- */
20
- import { createFieldPath as createBehaviorFieldPath } from './create-field-path';
21
- import { FormErrorHandler, ErrorStrategy } from '../utils/error-handler';
22
- /**
23
- * Класс для применения behavior схемы к форме
24
- *
25
- * Выполняет:
26
- * 1. Начало регистрации behaviors (beginRegistration)
27
- * 2. Выполнение схемы (регистрация behaviors)
28
- * 3. Завершение регистрации (endRegistration) - применение behaviors
29
- * 4. Возврат функции cleanup для отписки
30
- *
31
- * @template T Тип формы (объект)
32
- */
33
- export class BehaviorApplicator {
34
- form;
35
- behaviorRegistry;
36
- constructor(form, behaviorRegistry) {
37
- this.form = form;
38
- this.behaviorRegistry = behaviorRegistry;
39
- }
40
- /**
41
- * Применить behavior схему к форме
42
- *
43
- * Этапы:
44
- * 1. Начать регистрацию (beginRegistration)
45
- * 2. Выполнить схему (регистрация behaviors)
46
- * 3. Завершить регистрацию (endRegistration) - применить behaviors
47
- * 4. Вернуть функцию cleanup для отписки
48
- *
49
- * @param schemaFn Функция-схема behavior
50
- * @returns Функция отписки от всех behaviors
51
- *
52
- * @example
53
- * ```typescript
54
- * const cleanup = behaviorApplicator.apply((path) => {
55
- * copyFrom(path.residenceAddress, path.registrationAddress, {
56
- * when: (form) => form.sameAsRegistration === true
57
- * });
58
- *
59
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
60
- *
61
- * computeFrom(
62
- * path.initialPayment,
63
- * [path.propertyValue],
64
- * (propertyValue) => propertyValue ? propertyValue * 0.2 : null
65
- * );
66
- * });
67
- *
68
- * // Cleanup при unmount
69
- * useEffect(() => cleanup, []);
70
- * ```
71
- */
72
- apply(schemaFn) {
73
- this.behaviorRegistry.beginRegistration();
74
- try {
75
- // 1. Создать field path для type-safe доступа к полям
76
- const path = createBehaviorFieldPath();
77
- // 2. Выполнить схему (регистрация behaviors)
78
- schemaFn(path);
79
- // 3. Завершить регистрацию и применить behaviors
80
- // Используем публичный метод getProxy() для получения proxy-инстанса
81
- const formToUse = this.form.getProxy();
82
- const result = this.behaviorRegistry.endRegistration(formToUse);
83
- // 4. Вернуть функцию cleanup
84
- return result.cleanup;
85
- }
86
- catch (error) {
87
- FormErrorHandler.handle(error, 'BehaviorApplicator', ErrorStrategy.THROW);
88
- // TypeScript требует return, но код никогда не дойдет сюда
89
- throw error;
90
- }
91
- }
92
- }
@@ -1,38 +0,0 @@
1
- /**
2
- * BehaviorContext - контекст для behavior callback функций
3
- *
4
- * Реализует FormContext для behavior схем
5
- */
6
- /**
7
- * Реализация BehaviorContext (FormContext)
8
- *
9
- * Предоставляет:
10
- * - `form` - прямой типизированный доступ к форме
11
- * - `setFieldValue` - безопасная установка значения (emitEvent: false)
12
- */
13
- export class BehaviorContextImpl {
14
- /**
15
- * Форма с типизированным Proxy-доступом к полям
16
- */
17
- form;
18
- _form;
19
- constructor(form) {
20
- this._form = form;
21
- // Используем _proxyInstance если доступен, иначе fallback на form
22
- const proxy = (form
23
- ._proxyInstance || form);
24
- this.form = proxy;
25
- }
26
- /**
27
- * Безопасно установить значение поля по строковому пути
28
- *
29
- * Автоматически использует emitEvent: false для предотвращения циклов
30
- */
31
- setFieldValue(path, value) {
32
- const node = this._form.getFieldByPath(path);
33
- if (node) {
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- node.setValue(value, { emitEvent: false });
36
- }
37
- }
38
- }
@@ -1,198 +0,0 @@
1
- /**
2
- * BehaviorRegistry - регистрация и управление behavior схемами
3
- *
4
- * Аналогично ValidationRegistry, но для реактивного поведения форм
5
- */
6
- import { BehaviorContextImpl } from './behavior-context';
7
- import { RegistryStack } from '../utils/registry-stack';
8
- /**
9
- * Реестр behaviors для формы
10
- *
11
- * Каждый экземпляр GroupNode создает собственный реестр (композиция).
12
- * Устраняет race conditions и изолирует формы друг от друга.
13
- *
14
- * Context stack используется для tracking текущего активного реестра:
15
- * - beginRegistration() помещает this в stack
16
- * - endRegistration() извлекает из stack
17
- * - getCurrent() возвращает текущий активный реестр
18
- *
19
- * @example
20
- * ```typescript
21
- * class GroupNode {
22
- * private readonly behaviorRegistry = new BehaviorRegistry();
23
- *
24
- * applyBehaviorSchema(schemaFn) {
25
- * this.behaviorRegistry.beginRegistration(); // Pushes this to stack
26
- * schemaFn(createBehaviorFieldPath(this)); // Uses getCurrent()
27
- * return this.behaviorRegistry.endRegistration(this); // Pops from stack
28
- * }
29
- * }
30
- * ```
31
- */
32
- export class BehaviorRegistry {
33
- /**
34
- * Stack активных контекстов регистрации
35
- * Используется для изоляции форм друг от друга
36
- */
37
- static contextStack = new RegistryStack();
38
- registrations = [];
39
- isRegistering = false;
40
- /**
41
- * Получить текущий активный реестр из context stack
42
- *
43
- * @returns Текущий активный реестр или null
44
- *
45
- * @example
46
- * ```typescript
47
- * // В schema-behaviors.ts
48
- * export function copyFrom(...) {
49
- * const registry = BehaviorRegistry.getCurrent();
50
- * if (registry) {
51
- * registry.register({ ... });
52
- * }
53
- * }
54
- * ```
55
- */
56
- static getCurrent() {
57
- return BehaviorRegistry.contextStack.getCurrent();
58
- }
59
- /**
60
- * Начать регистрацию behaviors
61
- * Вызывается перед применением схемы
62
- *
63
- * Помещает this в context stack для изоляции форм
64
- */
65
- beginRegistration() {
66
- this.isRegistering = true;
67
- this.registrations = [];
68
- // Помещаем this в stack для tracking текущего активного реестра
69
- BehaviorRegistry.contextStack.push(this);
70
- }
71
- /**
72
- * Зарегистрировать behavior handler
73
- * Вызывается функциями из schema-behaviors.ts
74
- *
75
- * @param handler - BehaviorHandlerFn функция
76
- * @param options - Опции behavior (debounce)
77
- *
78
- * @example
79
- * ```typescript
80
- * const handler = createCopyBehavior(target, source, { when: ... });
81
- * registry.register(handler, { debounce: 300 });
82
- * ```
83
- */
84
- register(handler, options) {
85
- if (!this.isRegistering) {
86
- if (import.meta.env.DEV) {
87
- throw new Error('BehaviorRegistry: call beginRegistration() before registering behaviors');
88
- }
89
- return;
90
- }
91
- this.registrations.push({
92
- // Type assertion безопасен: handler будет вызван с правильным типом формы в createEffect
93
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
- handler: handler,
95
- debounce: options?.debounce,
96
- });
97
- }
98
- /**
99
- * Завершить регистрацию и применить behaviors к форме
100
- * Создает effect подписки для всех зарегистрированных behaviors
101
- *
102
- * Извлекает this из context stack
103
- *
104
- * @param form - GroupNode формы
105
- * @returns Количество зарегистрированных behaviors и функция cleanup
106
- */
107
- endRegistration(form) {
108
- this.isRegistering = false;
109
- // Извлекаем из stack с проверкой
110
- BehaviorRegistry.contextStack.verify(this, 'BehaviorRegistry');
111
- const context = new BehaviorContextImpl(form);
112
- const disposeCallbacks = [];
113
- // Создаем effect подписки для каждого behavior
114
- for (const registered of this.registrations) {
115
- const dispose = this.createEffect(registered, form, context);
116
- if (dispose) {
117
- disposeCallbacks.push(dispose);
118
- }
119
- }
120
- // Функция cleanup для отписки от всех effects
121
- const cleanup = () => {
122
- disposeCallbacks.forEach((dispose) => dispose());
123
- };
124
- return {
125
- count: this.registrations.length,
126
- cleanup,
127
- };
128
- }
129
- /**
130
- * Создать effect подписку для behavior
131
- * @private
132
- */
133
- createEffect(registered, form, context) {
134
- const { handler, debounce: debounceMs = 0 } = registered;
135
- let debounceTimer = null;
136
- // Обертка для debounce
137
- const withDebounce = (callback) => {
138
- if (debounceMs > 0) {
139
- if (debounceTimer)
140
- clearTimeout(debounceTimer);
141
- debounceTimer = setTimeout(callback, debounceMs);
142
- }
143
- else {
144
- callback();
145
- }
146
- };
147
- // Cleanup функция для debounce таймера
148
- const cleanupDebounce = () => {
149
- if (debounceTimer) {
150
- clearTimeout(debounceTimer);
151
- debounceTimer = null;
152
- }
153
- };
154
- // Вызываем handler напрямую
155
- // Type assertion необходим из-за contravariance: handler хранится как
156
- // BehaviorHandlerFn<FormFields>, но вызывается с более специфичным типом T.
157
- // Используем any для обхода ограничений TypeScript при хранении generic handlers в массиве.
158
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
- const effectDispose = handler(form, context, withDebounce);
160
- if (!effectDispose) {
161
- return null;
162
- }
163
- // Возвращаем комбинированный cleanup
164
- // который очищает и effect, и debounce таймер
165
- return () => {
166
- cleanupDebounce();
167
- if (effectDispose) {
168
- effectDispose();
169
- }
170
- };
171
- }
172
- }
173
- // ============================================================================
174
- // Глобальный экземпляр BehaviorRegistry УДАЛЕН
175
- // ============================================================================
176
- //
177
- // Ранее здесь был глобальный Singleton экземпляр BehaviorRegistry,
178
- // который создавал race conditions и нарушал изоляцию форм.
179
- //
180
- // Теперь каждый GroupNode создает собственный экземпляр BehaviorRegistry:
181
- //
182
- // @example
183
- // ```typescript
184
- // class GroupNode {
185
- // private readonly behaviorRegistry = new BehaviorRegistry();
186
- //
187
- // applyBehaviorSchema(schemaFn) {
188
- // this.behaviorRegistry.beginRegistration();
189
- // schemaFn(createBehaviorFieldPath(this));
190
- // return this.behaviorRegistry.endRegistration(this);
191
- // }
192
- // }
193
- // ```
194
- //
195
- // Это обеспечивает:
196
- // - Полную изоляцию форм друг от друга
197
- // - Отсутствие race conditions при параллельной регистрации
198
- // - Возможность применять разные behavior схемы к разным формам одновременно
@@ -1,84 +0,0 @@
1
- /**
2
- * Вычисляемые поля
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/computeFrom
7
- */
8
- import { effect } from '@preact/signals-core';
9
- import { getCurrentBehaviorRegistry } from '../../utils/registry-helpers';
10
- /**
11
- * Автоматически вычисляет значение поля на основе других полей
12
- *
13
- * @group Behaviors
14
- * @category Behavior Rules
15
- *
16
- * @param sources - Массив полей-зависимостей
17
- * @param target - Поле для записи результата
18
- * @param computeFn - Функция вычисления (принимает объект с именами полей)
19
- * @param options - Опции
20
- *
21
- * @example
22
- * ```typescript
23
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
24
- * // Автоматический расчет минимального взноса
25
- * computeFrom(
26
- * [path.propertyValue],
27
- * path.initialPayment,
28
- * (values) => values.propertyValue ? values.propertyValue * 0.2 : null,
29
- * { debounce: 300 }
30
- * );
31
- *
32
- * // Общая стоимость = цена * количество
33
- * computeFrom(
34
- * [path.price, path.quantity],
35
- * path.total,
36
- * (values) => values.price * values.quantity
37
- * );
38
- * };
39
- * ```
40
- */
41
- export function computeFrom(
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- sources, target, computeFn, options) {
44
- const { debounce, condition } = options || {};
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- const handler = (form, _context, withDebounce) => {
47
- const targetNode = form.getFieldByPath(target.__path);
48
- if (!targetNode)
49
- return null;
50
- // Разрешаем source узлы
51
- const sourceNodes = sources
52
- .map((field) => form.getFieldByPath(field.__path))
53
- .filter((node) => node !== undefined);
54
- if (sourceNodes.length === 0)
55
- return null;
56
- return effect(() => {
57
- // Читаем значения всех source полей
58
- const sourceValues = sourceNodes.map((node) => node.value.value);
59
- withDebounce(() => {
60
- // Проверка условия
61
- if (condition) {
62
- const formValue = form.getValue();
63
- if (!condition(formValue))
64
- return;
65
- }
66
- // Создаем объект с именами полей для computeFn
67
- // computeFn ожидает объект вида { fieldName: value, ... }
68
- const sourceValuesObject = {};
69
- sources.forEach((source, index) => {
70
- // Извлекаем имя поля из пути (последний сегмент)
71
- const fieldName = source.__path.split('.').pop() || source.__path;
72
- sourceValuesObject[fieldName] = sourceValues[index];
73
- });
74
- // Вычисляем новое значение
75
- const computedValue = computeFn(sourceValuesObject);
76
- // Устанавливаем значение без триггера событий
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- targetNode.setValue(computedValue, { emitEvent: false });
79
- });
80
- });
81
- };
82
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
- getCurrentBehaviorRegistry().register(handler, { debounce });
84
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * Копирование значений между полями
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/copyFrom
7
- */
8
- import { watchField } from './watch-field';
9
- /**
10
- * Копирует значения из одного поля/группы в другое при выполнении условия
11
- *
12
- * @group Behaviors
13
- * @category Behavior Rules
14
- *
15
- * @param source - Откуда копировать
16
- * @param target - Куда копировать
17
- * @param options - Опции копирования
18
- *
19
- * @example
20
- * ```typescript
21
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
22
- * // Копировать адрес регистрации в адрес проживания
23
- * copyFrom(path.registrationAddress, path.residenceAddress, {
24
- * when: (form) => form.sameAsRegistration === true,
25
- * fields: 'all'
26
- * });
27
- * };
28
- * ```
29
- */
30
- export function copyFrom(source, target, options) {
31
- const { when, fields = 'all', transform, debounce } = options || {};
32
- watchField(source, (sourceValue, ctx) => {
33
- // Проверка условия
34
- if (when) {
35
- const formValue = ctx.form.getValue();
36
- if (!when(formValue))
37
- return;
38
- }
39
- // Трансформация значения
40
- const value = transform ? transform(sourceValue) : sourceValue;
41
- // Получаем target node
42
- const targetNode = ctx.form.getFieldByPath(target.__path);
43
- if (!targetNode)
44
- return;
45
- // Копирование
46
- if (fields === 'all' || !fields) {
47
- targetNode.setValue(value, { emitEvent: false });
48
- }
49
- else {
50
- // Частичное копирование для групп
51
- const patch = {};
52
- fields.forEach((key) => {
53
- if (sourceValue && typeof sourceValue === 'object') {
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- patch[key] = sourceValue[key];
56
- }
57
- });
58
- if ('patchValue' in targetNode) {
59
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
- targetNode.patchValue(patch);
61
- }
62
- }
63
- }, { debounce });
64
- }
@@ -1,81 +0,0 @@
1
- /**
2
- * Условное включение/отключение полей
3
- *
4
- * @group Behaviors
5
- * @category Behavior Rules
6
- * @module behaviors/enableWhen
7
- */
8
- import { effect } from '@preact/signals-core';
9
- import { getCurrentBehaviorRegistry } from '../../utils/registry-helpers';
10
- /**
11
- * Условное включение поля на основе значений других полей
12
- *
13
- * @group Behaviors
14
- * @category Behavior Rules
15
- *
16
- * @param field - Поле для включения/выключения
17
- * @param condition - Функция условия (true = enable, false = disable)
18
- * @param options - Опции
19
- *
20
- * @example
21
- * ```typescript
22
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
23
- * // Включить поле только для ипотеки
24
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage', {
25
- * resetOnDisable: true
26
- * });
27
- * };
28
- * ```
29
- */
30
- export function enableWhen(
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
- field, condition, options) {
33
- const { debounce, resetOnDisable = false } = options || {};
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- const handler = (form, _context, withDebounce) => {
36
- const targetNode = form.getFieldByPath(field.__path);
37
- if (!targetNode)
38
- return null;
39
- return effect(() => {
40
- const formValue = form.value.value;
41
- withDebounce(() => {
42
- const shouldEnable = condition(formValue);
43
- if (shouldEnable) {
44
- targetNode.enable();
45
- }
46
- else {
47
- targetNode.disable();
48
- if (resetOnDisable) {
49
- targetNode.reset();
50
- }
51
- }
52
- });
53
- });
54
- };
55
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
- getCurrentBehaviorRegistry().register(handler, { debounce });
57
- }
58
- /**
59
- * Условное выключение поля (инверсия enableWhen)
60
- *
61
- * @group Behaviors
62
- * @category Behavior Rules
63
- *
64
- * @param field - Поле для выключения
65
- * @param condition - Функция условия (true = disable, false = enable)
66
- * @param options - Опции
67
- *
68
- * @example
69
- * ```typescript
70
- * const schema: BehaviorSchemaFn<MyForm> = (path) => {
71
- * // Выключить поле для потребительского кредита
72
- * disableWhen(path.propertyValue, (form) => form.loanType === 'consumer');
73
- * };
74
- * ```
75
- */
76
- export function disableWhen(
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- field, condition, options) {
79
- // Инвертируем условие
80
- enableWhen(field, (form) => !condition(form), options);
81
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Behavior rules
3
- */
4
- export { copyFrom } from './copy-from';
5
- export { enableWhen, disableWhen } from './enable-when';
6
- export { computeFrom } from './compute-from';
7
- export { watchField } from './watch-field';
8
- export { revalidateWhen } from './revalidate-when';
9
- export { syncFields } from './sync-fields';
10
- export { resetWhen } from './reset-when';
11
- export { transformValue, createTransformer, transformers, } from './transform-value';