@ng-forge/dynamic-forms 0.10.0-next.2 → 0.10.0-next.4
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/fesm2022/{ng-forge-dynamic-forms-array-field.component-PPh_uVVh.mjs → ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs} +4 -3
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-3dZDF3kh.mjs → ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs} +4 -3
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-B7kCznbc.mjs → ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs} +2 -2
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-DtWBauY3.mjs → ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs} +2 -2
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-CbY8WZZB.mjs → ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs} +2 -2
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-Dr8Z628-.mjs → ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs} +4 -3
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +14 -10
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +3372 -0
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs} +725 -3866
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-DoGKs3LN.mjs → ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs} +4 -3
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs → ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs} +1 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-schema.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C3TPVvj_.mjs → ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs} +2 -2
- package/fesm2022/ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms.mjs +2 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs.map +1 -1
- package/package.json +14 -1
- package/schematics/collection.json +10 -0
- package/schematics/generators.json +12 -0
- package/schematics/ng-add/index.js +87 -0
- package/schematics/ng-add/index.js.map +1 -0
- package/schematics/ng-add/nx-wrapper.js +5 -0
- package/schematics/ng-add/nx-wrapper.js.map +1 -0
- package/schematics/ng-add/recipes.js +60 -0
- package/schematics/ng-add/recipes.js.map +1 -0
- package/schematics/ng-add/rules/add-packages.js +73 -0
- package/schematics/ng-add/rules/add-packages.js.map +1 -0
- package/schematics/ng-add/rules/add-providers.js +132 -0
- package/schematics/ng-add/rules/add-providers.js.map +1 -0
- package/schematics/ng-add/rules/add-styles.js +123 -0
- package/schematics/ng-add/rules/add-styles.js.map +1 -0
- package/schematics/ng-add/schema.json +48 -0
- package/schematics/package.json +1 -0
- package/schematics/versions.js +33 -0
- package/schematics/versions.js.map +1 -0
- package/types/ng-forge-dynamic-forms-integration.d.ts +31 -464
- package/types/ng-forge-dynamic-forms-internal.d.ts +3508 -0
- package/types/ng-forge-dynamic-forms-schema.d.ts +2 -117
- package/types/ng-forge-dynamic-forms.d.ts +87 -7170
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-PPh_uVVh.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-3dZDF3kh.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-B7kCznbc.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DtWBauY3.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-CbY8WZZB.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-Dr8Z628-.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-DoGKs3LN.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-text-field.component-C3TPVvj_.mjs.map +0 -1
|
@@ -0,0 +1,3508 @@
|
|
|
1
|
+
import { Observable, OperatorFunction } from 'rxjs';
|
|
2
|
+
import { FieldContext, TreeValidationResult, ValidationError as ValidationError$1, FieldTree, FieldState, LogicFn, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Signal, ResourceRef, Injector, WritableSignal, InjectionToken, Type, ViewContainerRef, TemplateRef, PipeTransform, Resource } from '@angular/core';
|
|
5
|
+
import * as _ng_forge_dynamic_forms_internal from '@ng-forge/dynamic-forms/internal';
|
|
6
|
+
import { FormSchema } from '@ng-forge/dynamic-forms/schema';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Interface for dynamic forms logger implementations.
|
|
10
|
+
* Allows custom logging integrations (Sentry, DataDog, etc.)
|
|
11
|
+
*/
|
|
12
|
+
interface Logger {
|
|
13
|
+
/**
|
|
14
|
+
* Log a debug message.
|
|
15
|
+
* Use for detailed diagnostic information during development.
|
|
16
|
+
*/
|
|
17
|
+
debug(message: string, ...args: unknown[]): void;
|
|
18
|
+
/**
|
|
19
|
+
* Log an info message.
|
|
20
|
+
* Use for general operational information.
|
|
21
|
+
*/
|
|
22
|
+
info(message: string, ...args: unknown[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Log a warning message.
|
|
25
|
+
* Use for potentially problematic situations that don't prevent operation.
|
|
26
|
+
*/
|
|
27
|
+
warn(message: string, ...args: unknown[]): void;
|
|
28
|
+
/**
|
|
29
|
+
* Log an error message.
|
|
30
|
+
* Use for error conditions that may affect functionality.
|
|
31
|
+
*/
|
|
32
|
+
error(message: string, ...args: unknown[]): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Shared shape for DI-scoped warning trackers that dedupe log output.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
interface WarningTracker {
|
|
41
|
+
warnedKeys: Set<string>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Creates a fresh warning tracker instance with an empty key set.
|
|
45
|
+
* DI-scoped per form instance — never module-scoped (SSR-unsafe).
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
declare function createWarningTracker(): WarningTracker;
|
|
50
|
+
|
|
51
|
+
/** Represents the state information of a single form field. */
|
|
52
|
+
interface FieldStateInfo {
|
|
53
|
+
readonly touched: boolean;
|
|
54
|
+
readonly dirty: boolean;
|
|
55
|
+
/** Convenience property: equivalent to `!dirty` */
|
|
56
|
+
readonly pristine: boolean;
|
|
57
|
+
readonly valid: boolean;
|
|
58
|
+
readonly invalid: boolean;
|
|
59
|
+
readonly pending: boolean;
|
|
60
|
+
/** Whether the field is currently hidden. */
|
|
61
|
+
readonly hidden: boolean;
|
|
62
|
+
/** Whether the field is currently readonly. */
|
|
63
|
+
readonly readonly: boolean;
|
|
64
|
+
/** Whether the field is currently disabled. */
|
|
65
|
+
readonly disabled: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** Map of field keys to their state information. */
|
|
68
|
+
type FormFieldStateMap = Record<string, FieldStateInfo | undefined>;
|
|
69
|
+
/** Field state context for the current field being evaluated. */
|
|
70
|
+
type FieldStateContext = FieldStateInfo;
|
|
71
|
+
|
|
72
|
+
interface EvaluationContext<TValue = unknown, TFormValue extends Record<string, unknown> = any> {
|
|
73
|
+
/** Current field value */
|
|
74
|
+
fieldValue: TValue;
|
|
75
|
+
/** Form value for the current evaluation scope. */
|
|
76
|
+
formValue: TFormValue;
|
|
77
|
+
/** Field path for relative references */
|
|
78
|
+
fieldPath: string;
|
|
79
|
+
/** Custom evaluation functions */
|
|
80
|
+
customFunctions?: Record<string, (context: EvaluationContext) => unknown>;
|
|
81
|
+
/** Logger for diagnostic output */
|
|
82
|
+
logger: Logger;
|
|
83
|
+
/**
|
|
84
|
+
* Value of the field's nearest parent **group** (or array item, when the
|
|
85
|
+
* field has no inner group above the array boundary).
|
|
86
|
+
*/
|
|
87
|
+
groupValue?: unknown;
|
|
88
|
+
/** Root form value when inside an array context. */
|
|
89
|
+
rootFormValue?: Record<string, unknown>;
|
|
90
|
+
/** Current array index when inside an array derivation. */
|
|
91
|
+
arrayIndex?: number;
|
|
92
|
+
/** Path to the array field when inside an array derivation. */
|
|
93
|
+
arrayPath?: string;
|
|
94
|
+
/** External data signals resolved to their current values. */
|
|
95
|
+
externalData?: Record<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* DI-scoped tracker for deprecation warnings.
|
|
98
|
+
* Used by the condition evaluator to deduplicate deprecation warnings.
|
|
99
|
+
*/
|
|
100
|
+
deprecationTracker?: WarningTracker;
|
|
101
|
+
/** State of the current field being evaluated. */
|
|
102
|
+
fieldState?: FieldStateContext;
|
|
103
|
+
/** State of all fields in the form, keyed by field key. */
|
|
104
|
+
formFieldState?: FormFieldStateMap;
|
|
105
|
+
/** Allow additional properties for flexible expression evaluation */
|
|
106
|
+
[key: string]: unknown;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Scope of a custom function. */
|
|
110
|
+
type CustomFunctionScope = 'field' | 'form';
|
|
111
|
+
/** Options for registering a custom function. */
|
|
112
|
+
interface CustomFunctionOptions {
|
|
113
|
+
/**
|
|
114
|
+
* The scope of the function.
|
|
115
|
+
*
|
|
116
|
+
* @default 'form'
|
|
117
|
+
*/
|
|
118
|
+
scope?: CustomFunctionScope;
|
|
119
|
+
}
|
|
120
|
+
/** Custom function signature for conditional expressions */
|
|
121
|
+
type CustomFunction<TFormValue extends Record<string, unknown> = any> = (context: EvaluationContext<unknown, TFormValue>) => unknown;
|
|
122
|
+
|
|
123
|
+
/** Async function for derivations — returns any value. */
|
|
124
|
+
type AsyncDerivationFunction<TFormValue extends Record<string, unknown> = any> = (context: EvaluationContext<unknown, TFormValue>) => Promise<unknown> | Observable<unknown>;
|
|
125
|
+
/** Async function for conditions — returns boolean. */
|
|
126
|
+
type AsyncConditionFunction<TFormValue extends Record<string, unknown> = any> = (context: EvaluationContext<unknown, TFormValue>) => Promise<boolean> | Observable<boolean>;
|
|
127
|
+
|
|
128
|
+
/** Configuration for an HTTP request used by declarative HTTP validators, derivations, and conditions. */
|
|
129
|
+
interface HttpRequestConfig {
|
|
130
|
+
/** URL to send the request to */
|
|
131
|
+
url: string;
|
|
132
|
+
/** HTTP method. Defaults to 'GET' */
|
|
133
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
134
|
+
/**
|
|
135
|
+
* URL path parameters. Keys correspond to `:key` placeholders in the URL.
|
|
136
|
+
* Values are expressions evaluated by ExpressionParser against the current EvaluationContext.
|
|
137
|
+
*/
|
|
138
|
+
params?: Record<string, string>;
|
|
139
|
+
/**
|
|
140
|
+
* Query parameters appended to the URL.
|
|
141
|
+
* Values are expressions evaluated by ExpressionParser against the current EvaluationContext.
|
|
142
|
+
*/
|
|
143
|
+
queryParams?: Record<string, string>;
|
|
144
|
+
/** Request body (for POST/PUT/PATCH) */
|
|
145
|
+
body?: Record<string, unknown>;
|
|
146
|
+
/**
|
|
147
|
+
* When true, top-level string values in `body` are treated as expressions
|
|
148
|
+
* and evaluated by ExpressionParser (shallow only — nested objects are passed through as-is).
|
|
149
|
+
*/
|
|
150
|
+
evaluateBodyExpressions?: boolean;
|
|
151
|
+
/** HTTP headers to include in the request */
|
|
152
|
+
headers?: Record<string, string>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Comparison operators for field value and form value conditions. */
|
|
156
|
+
type ComparisonOperator = 'equals' | 'notEquals' | 'greater' | 'less' | 'greaterOrEqual' | 'lessOrEqual' | 'contains' | 'startsWith' | 'endsWith' | 'matches';
|
|
157
|
+
/** Condition that compares a specific field's value against an expected value. */
|
|
158
|
+
interface FieldValueCondition {
|
|
159
|
+
type: 'fieldValue';
|
|
160
|
+
/** Path to the field whose value to compare */
|
|
161
|
+
fieldPath: string;
|
|
162
|
+
/** Comparison operator */
|
|
163
|
+
operator: ComparisonOperator;
|
|
164
|
+
/** Value to compare against */
|
|
165
|
+
value?: unknown;
|
|
166
|
+
}
|
|
167
|
+
/** Condition that invokes a custom function. */
|
|
168
|
+
type CustomCondition = {
|
|
169
|
+
type: 'custom';
|
|
170
|
+
/** Name of the registered custom function to invoke */
|
|
171
|
+
functionName: string;
|
|
172
|
+
/** Inline form is forbidden when `functionName` is set */
|
|
173
|
+
fn?: never;
|
|
174
|
+
} | {
|
|
175
|
+
type: 'custom';
|
|
176
|
+
/** Inline custom function. Mutually exclusive with `functionName`. */
|
|
177
|
+
fn: CustomFunction;
|
|
178
|
+
/** Registered form is forbidden when `fn` is set */
|
|
179
|
+
functionName?: never;
|
|
180
|
+
};
|
|
181
|
+
/** Condition that evaluates a JavaScript expression using the secure AST-based parser. */
|
|
182
|
+
interface JavascriptCondition {
|
|
183
|
+
type: 'javascript';
|
|
184
|
+
/** JavaScript expression string to evaluate */
|
|
185
|
+
expression: string;
|
|
186
|
+
}
|
|
187
|
+
/** Condition that evaluates based on an HTTP response from a remote server. */
|
|
188
|
+
interface HttpCondition {
|
|
189
|
+
type: 'http';
|
|
190
|
+
/** HTTP request configuration */
|
|
191
|
+
http: HttpRequestConfig;
|
|
192
|
+
/**
|
|
193
|
+
* Expression to extract boolean from response (scope: `{ response }`).
|
|
194
|
+
* e.g. `'response.allowed'`, `'response.permissions.canEdit'`
|
|
195
|
+
* When omitted, the entire response is coerced to boolean: `!!response`
|
|
196
|
+
*/
|
|
197
|
+
responseExpression?: string;
|
|
198
|
+
/**
|
|
199
|
+
* Value to return while the HTTP request is in-flight.
|
|
200
|
+
*
|
|
201
|
+
* @default false
|
|
202
|
+
*/
|
|
203
|
+
pendingValue?: boolean;
|
|
204
|
+
/** Cache duration in ms for HTTP responses. @default 30000 */
|
|
205
|
+
cacheDurationMs?: number;
|
|
206
|
+
/**
|
|
207
|
+
* Debounce time in milliseconds for re-evaluation when dependent form values change.
|
|
208
|
+
*
|
|
209
|
+
* @default 300
|
|
210
|
+
*/
|
|
211
|
+
debounceMs?: number;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Shared fields for both branches of {@link AsyncCondition}.
|
|
215
|
+
*
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
interface AsyncConditionBase {
|
|
219
|
+
type: 'async';
|
|
220
|
+
/**
|
|
221
|
+
* Value to return while async resolution is pending.
|
|
222
|
+
*
|
|
223
|
+
* @default false
|
|
224
|
+
*/
|
|
225
|
+
pendingValue?: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Debounce ms for re-evaluation.
|
|
228
|
+
*
|
|
229
|
+
* @default 300
|
|
230
|
+
*/
|
|
231
|
+
debounceMs?: number;
|
|
232
|
+
}
|
|
233
|
+
/** Condition that resolves asynchronously via a custom function. */
|
|
234
|
+
type AsyncCondition = (AsyncConditionBase & {
|
|
235
|
+
/** Name of the registered async condition function */
|
|
236
|
+
asyncFunctionName: string;
|
|
237
|
+
/** Inline form is forbidden when `asyncFunctionName` is set */
|
|
238
|
+
asyncFn?: never;
|
|
239
|
+
}) | (AsyncConditionBase & {
|
|
240
|
+
/** Inline async condition function. Mutually exclusive with `asyncFunctionName`. */
|
|
241
|
+
asyncFn: AsyncConditionFunction;
|
|
242
|
+
/** Registered form is forbidden when `asyncFn` is set */
|
|
243
|
+
asyncFunctionName?: never;
|
|
244
|
+
});
|
|
245
|
+
/** Logical AND — all sub-conditions must be true. */
|
|
246
|
+
interface AndCondition {
|
|
247
|
+
type: 'and';
|
|
248
|
+
/** Sub-conditions that must all evaluate to true */
|
|
249
|
+
conditions: ConditionalExpression[];
|
|
250
|
+
}
|
|
251
|
+
/** Logical OR — at least one sub-condition must be true. */
|
|
252
|
+
interface OrCondition {
|
|
253
|
+
type: 'or';
|
|
254
|
+
/** Sub-conditions where at least one must evaluate to true */
|
|
255
|
+
conditions: ConditionalExpression[];
|
|
256
|
+
}
|
|
257
|
+
/** Discriminated union of all conditional expression types. */
|
|
258
|
+
type ConditionalExpression = FieldValueCondition | CustomCondition | JavascriptCondition | HttpCondition | AsyncCondition | AndCondition | OrCondition;
|
|
259
|
+
|
|
260
|
+
/** Special form-state conditions for button disabled logic. */
|
|
261
|
+
type FormStateCondition =
|
|
262
|
+
/** True when form.valid() === false */
|
|
263
|
+
'formInvalid'
|
|
264
|
+
/** True when form.submitting() === true */
|
|
265
|
+
| 'formSubmitting'
|
|
266
|
+
/** True when fields on the current page are invalid (for paged forms) */
|
|
267
|
+
| 'pageInvalid';
|
|
268
|
+
/** Logic type for controlling field state (hidden, readonly, disabled, required). */
|
|
269
|
+
type StateLogicType = 'hidden' | 'readonly' | 'disabled' | 'required';
|
|
270
|
+
/**
|
|
271
|
+
* Base configuration for conditional field state logic.
|
|
272
|
+
*
|
|
273
|
+
* @internal
|
|
274
|
+
*/
|
|
275
|
+
interface BaseStateLogicConfig {
|
|
276
|
+
/** Logic type identifier for field state. */
|
|
277
|
+
type: StateLogicType;
|
|
278
|
+
/** Condition that determines when this logic applies. */
|
|
279
|
+
condition: ConditionalExpression | boolean | FormStateCondition;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* State logic that evaluates immediately on change (default).
|
|
283
|
+
*
|
|
284
|
+
* @internal
|
|
285
|
+
*/
|
|
286
|
+
interface ImmediateStateLogicConfig extends BaseStateLogicConfig {
|
|
287
|
+
/**
|
|
288
|
+
* Trigger for immediate evaluation.
|
|
289
|
+
*
|
|
290
|
+
* @default 'onChange'
|
|
291
|
+
*/
|
|
292
|
+
trigger?: 'onChange';
|
|
293
|
+
/** Not allowed for onChange trigger */
|
|
294
|
+
debounceMs?: never;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* State logic that evaluates after a debounce period.
|
|
298
|
+
*
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
interface DebouncedStateLogicConfig extends BaseStateLogicConfig {
|
|
302
|
+
/**
|
|
303
|
+
* Trigger for debounced evaluation.
|
|
304
|
+
* Evaluates after the value has stabilized for the debounce duration.
|
|
305
|
+
*/
|
|
306
|
+
trigger: 'debounced';
|
|
307
|
+
/**
|
|
308
|
+
* Debounce duration in milliseconds.
|
|
309
|
+
*
|
|
310
|
+
* @default 500
|
|
311
|
+
*/
|
|
312
|
+
debounceMs?: number;
|
|
313
|
+
}
|
|
314
|
+
/** Configuration for conditional field state logic. */
|
|
315
|
+
type StateLogicConfig = ImmediateStateLogicConfig | DebouncedStateLogicConfig;
|
|
316
|
+
/** Trigger timing for when logic is evaluated. */
|
|
317
|
+
type LogicTrigger = 'onChange' | 'debounced';
|
|
318
|
+
/**
|
|
319
|
+
* Shared fields that appear on all derivation logic config variants.
|
|
320
|
+
*
|
|
321
|
+
* @internal
|
|
322
|
+
*/
|
|
323
|
+
interface SharedDerivationFields {
|
|
324
|
+
/** Logic type identifier for value derivation. */
|
|
325
|
+
type: 'derivation';
|
|
326
|
+
/** Target property name for property derivation. */
|
|
327
|
+
targetProperty?: string;
|
|
328
|
+
/** Optional name for this derivation for debugging purposes. */
|
|
329
|
+
debugName?: string;
|
|
330
|
+
/** Condition that determines when this derivation applies. */
|
|
331
|
+
condition?: ConditionalExpression | boolean;
|
|
332
|
+
/**
|
|
333
|
+
* When true, the derivation stops running after the user manually
|
|
334
|
+
* edits the target field.
|
|
335
|
+
*/
|
|
336
|
+
stopOnUserOverride?: boolean;
|
|
337
|
+
/**
|
|
338
|
+
* When true (and `stopOnUserOverride` is also true), clears the
|
|
339
|
+
* user-override flag when any dependency of this derivation changes,
|
|
340
|
+
* allowing the derivation to run again.
|
|
341
|
+
*/
|
|
342
|
+
reEngageOnDependencyChange?: boolean;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Trigger variants for derivation timing.
|
|
346
|
+
*
|
|
347
|
+
* @internal
|
|
348
|
+
*/
|
|
349
|
+
type ImmediateDerivationTrigger = {
|
|
350
|
+
trigger?: 'onChange';
|
|
351
|
+
debounceMs?: never;
|
|
352
|
+
};
|
|
353
|
+
type DebouncedDerivationTrigger = {
|
|
354
|
+
trigger: 'debounced';
|
|
355
|
+
debounceMs?: number;
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Base for HTTP derivations. `source: 'http'` is required.
|
|
359
|
+
* TypeScript enforces `dependsOn` and `responseExpression` at the type level.
|
|
360
|
+
*
|
|
361
|
+
* @internal
|
|
362
|
+
*/
|
|
363
|
+
interface HttpDerivationBase extends SharedDerivationFields {
|
|
364
|
+
/** Identifies this derivation as HTTP-driven. */
|
|
365
|
+
source: 'http';
|
|
366
|
+
/** Forbidden on HTTP source */
|
|
367
|
+
fn?: never;
|
|
368
|
+
/** Forbidden on HTTP source */
|
|
369
|
+
asyncFn?: never;
|
|
370
|
+
/** HTTP request configuration for server-driven derivations. */
|
|
371
|
+
http: HttpRequestConfig;
|
|
372
|
+
/**
|
|
373
|
+
* Explicit field dependencies. Required for HTTP derivations to prevent
|
|
374
|
+
* wildcard triggering on every keystroke.
|
|
375
|
+
*/
|
|
376
|
+
dependsOn: string[];
|
|
377
|
+
/** Expression to extract the derived value from the HTTP response. */
|
|
378
|
+
responseExpression: string;
|
|
379
|
+
value?: never;
|
|
380
|
+
expression?: never;
|
|
381
|
+
functionName?: never;
|
|
382
|
+
asyncFunctionName?: never;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Shared fields for both branches of {@link AsyncFunctionDerivationBase}.
|
|
386
|
+
*
|
|
387
|
+
* @internal
|
|
388
|
+
*/
|
|
389
|
+
interface AsyncFunctionDerivationShared extends SharedDerivationFields {
|
|
390
|
+
/** Identifies this derivation as async-function-driven. */
|
|
391
|
+
source: 'asyncFunction';
|
|
392
|
+
/** Forbidden on asyncFunction source */
|
|
393
|
+
fn?: never;
|
|
394
|
+
/**
|
|
395
|
+
* Explicit field dependencies. Required for async derivations to prevent
|
|
396
|
+
* wildcard triggering on every form change.
|
|
397
|
+
*/
|
|
398
|
+
dependsOn: string[];
|
|
399
|
+
value?: never;
|
|
400
|
+
expression?: never;
|
|
401
|
+
functionName?: never;
|
|
402
|
+
http?: never;
|
|
403
|
+
responseExpression?: never;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Base for async function derivations. `source: 'asyncFunction'` is required.
|
|
407
|
+
* TypeScript enforces `dependsOn` at the type level, and `asyncFunctionName` /
|
|
408
|
+
* `asyncFn` are mutually exclusive (XOR).
|
|
409
|
+
*
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
type AsyncFunctionDerivationBase = (AsyncFunctionDerivationShared & {
|
|
413
|
+
/** Name of a registered async derivation function. */
|
|
414
|
+
asyncFunctionName: string;
|
|
415
|
+
/** Inline form is forbidden when `asyncFunctionName` is set */
|
|
416
|
+
asyncFn?: never;
|
|
417
|
+
}) | (AsyncFunctionDerivationShared & {
|
|
418
|
+
/** Inline async derivation function. Mutually exclusive with `asyncFunctionName`. */
|
|
419
|
+
asyncFn: AsyncDerivationFunction;
|
|
420
|
+
/** Registered form is forbidden when `asyncFn` is set */
|
|
421
|
+
asyncFunctionName?: never;
|
|
422
|
+
});
|
|
423
|
+
/**
|
|
424
|
+
* Base for expression derivations.
|
|
425
|
+
*
|
|
426
|
+
* @internal
|
|
427
|
+
*/
|
|
428
|
+
interface ExpressionDerivationBase extends SharedDerivationFields {
|
|
429
|
+
source?: never;
|
|
430
|
+
/** Forbidden on expression mode */
|
|
431
|
+
fn?: never;
|
|
432
|
+
/** Forbidden on expression mode */
|
|
433
|
+
asyncFn?: never;
|
|
434
|
+
/** JavaScript expression to evaluate for the derived value. */
|
|
435
|
+
expression: string;
|
|
436
|
+
/** Explicit field dependencies for expressions. */
|
|
437
|
+
dependsOn?: string[];
|
|
438
|
+
value?: never;
|
|
439
|
+
functionName?: never;
|
|
440
|
+
http?: never;
|
|
441
|
+
asyncFunctionName?: never;
|
|
442
|
+
responseExpression?: never;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Base for value derivations.
|
|
446
|
+
*
|
|
447
|
+
* @internal
|
|
448
|
+
*/
|
|
449
|
+
interface ValueDerivationBase extends SharedDerivationFields {
|
|
450
|
+
source?: never;
|
|
451
|
+
/** Forbidden on value mode */
|
|
452
|
+
fn?: never;
|
|
453
|
+
/** Forbidden on value mode */
|
|
454
|
+
asyncFn?: never;
|
|
455
|
+
/** Static value to set on this field. */
|
|
456
|
+
value: unknown;
|
|
457
|
+
/** Explicit field dependencies for value derivations. */
|
|
458
|
+
dependsOn?: string[];
|
|
459
|
+
expression?: never;
|
|
460
|
+
functionName?: never;
|
|
461
|
+
http?: never;
|
|
462
|
+
asyncFunctionName?: never;
|
|
463
|
+
responseExpression?: never;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Shared fields for both branches of {@link FunctionDerivationBase}.
|
|
467
|
+
*
|
|
468
|
+
* @internal
|
|
469
|
+
*/
|
|
470
|
+
interface FunctionDerivationShared extends SharedDerivationFields {
|
|
471
|
+
source?: never;
|
|
472
|
+
/** Forbidden on function-derivation mode */
|
|
473
|
+
asyncFn?: never;
|
|
474
|
+
/** Explicit field dependencies for function derivations. */
|
|
475
|
+
dependsOn?: string[];
|
|
476
|
+
value?: never;
|
|
477
|
+
expression?: never;
|
|
478
|
+
http?: never;
|
|
479
|
+
asyncFunctionName?: never;
|
|
480
|
+
responseExpression?: never;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Base for custom sync function derivations. `functionName` and `fn` are
|
|
484
|
+
* mutually exclusive (XOR).
|
|
485
|
+
*
|
|
486
|
+
* @internal
|
|
487
|
+
*/
|
|
488
|
+
type FunctionDerivationBase = (FunctionDerivationShared & {
|
|
489
|
+
/** Name of a registered custom derivation function. */
|
|
490
|
+
functionName: string;
|
|
491
|
+
/** Inline form is forbidden when `functionName` is set */
|
|
492
|
+
fn?: never;
|
|
493
|
+
}) | (FunctionDerivationShared & {
|
|
494
|
+
/** Inline custom derivation function. Mutually exclusive with `functionName`. */
|
|
495
|
+
fn: CustomFunction;
|
|
496
|
+
/** Registered form is forbidden when `fn` is set */
|
|
497
|
+
functionName?: never;
|
|
498
|
+
});
|
|
499
|
+
/**
|
|
500
|
+
* HTTP derivation that evaluates immediately on change (default).
|
|
501
|
+
*
|
|
502
|
+
* @internal
|
|
503
|
+
*/
|
|
504
|
+
type OnChangeHttpDerivationLogicConfig = HttpDerivationBase & ImmediateDerivationTrigger;
|
|
505
|
+
/**
|
|
506
|
+
* HTTP derivation that evaluates after a debounce period.
|
|
507
|
+
*
|
|
508
|
+
* @internal
|
|
509
|
+
*/
|
|
510
|
+
type DebouncedHttpDerivationLogicConfig = HttpDerivationBase & DebouncedDerivationTrigger;
|
|
511
|
+
/**
|
|
512
|
+
* Async function derivation that evaluates immediately on change (default).
|
|
513
|
+
*
|
|
514
|
+
* @internal
|
|
515
|
+
*/
|
|
516
|
+
type OnChangeAsyncFunctionDerivationLogicConfig = AsyncFunctionDerivationBase & ImmediateDerivationTrigger;
|
|
517
|
+
/**
|
|
518
|
+
* Async function derivation that evaluates after a debounce period.
|
|
519
|
+
*
|
|
520
|
+
* @internal
|
|
521
|
+
*/
|
|
522
|
+
type DebouncedAsyncFunctionDerivationLogicConfig = AsyncFunctionDerivationBase & DebouncedDerivationTrigger;
|
|
523
|
+
/**
|
|
524
|
+
* Expression derivation that evaluates immediately on change (default).
|
|
525
|
+
*
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
528
|
+
type OnChangeExpressionDerivationLogicConfig = ExpressionDerivationBase & ImmediateDerivationTrigger;
|
|
529
|
+
/**
|
|
530
|
+
* Expression derivation that evaluates after a debounce period.
|
|
531
|
+
*
|
|
532
|
+
* @internal
|
|
533
|
+
*/
|
|
534
|
+
type DebouncedExpressionDerivationLogicConfig = ExpressionDerivationBase & DebouncedDerivationTrigger;
|
|
535
|
+
/**
|
|
536
|
+
* Value derivation that evaluates immediately on change (default).
|
|
537
|
+
*
|
|
538
|
+
* @internal
|
|
539
|
+
*/
|
|
540
|
+
type OnChangeValueDerivationLogicConfig = ValueDerivationBase & ImmediateDerivationTrigger;
|
|
541
|
+
/**
|
|
542
|
+
* Value derivation that evaluates after a debounce period.
|
|
543
|
+
*
|
|
544
|
+
* @internal
|
|
545
|
+
*/
|
|
546
|
+
type DebouncedValueDerivationLogicConfig = ValueDerivationBase & DebouncedDerivationTrigger;
|
|
547
|
+
/**
|
|
548
|
+
* Function derivation that evaluates immediately on change (default).
|
|
549
|
+
*
|
|
550
|
+
* @internal
|
|
551
|
+
*/
|
|
552
|
+
type OnChangeFunctionDerivationLogicConfig = FunctionDerivationBase & ImmediateDerivationTrigger;
|
|
553
|
+
/**
|
|
554
|
+
* Function derivation that evaluates after a debounce period.
|
|
555
|
+
*
|
|
556
|
+
* @internal
|
|
557
|
+
*/
|
|
558
|
+
type DebouncedFunctionDerivationLogicConfig = FunctionDerivationBase & DebouncedDerivationTrigger;
|
|
559
|
+
/** Configuration for value derivation logic. */
|
|
560
|
+
type DerivationLogicConfig = OnChangeHttpDerivationLogicConfig | DebouncedHttpDerivationLogicConfig | OnChangeAsyncFunctionDerivationLogicConfig | DebouncedAsyncFunctionDerivationLogicConfig | OnChangeExpressionDerivationLogicConfig | DebouncedExpressionDerivationLogicConfig | OnChangeValueDerivationLogicConfig | DebouncedValueDerivationLogicConfig | OnChangeFunctionDerivationLogicConfig | DebouncedFunctionDerivationLogicConfig;
|
|
561
|
+
/** Union type for all logic configurations. */
|
|
562
|
+
type LogicConfig = StateLogicConfig | DerivationLogicConfig;
|
|
563
|
+
/** Log level for derivation debug output. */
|
|
564
|
+
type DerivationLogLevel = 'none' | 'summary' | 'verbose';
|
|
565
|
+
/** Configuration for derivation debug logging. */
|
|
566
|
+
interface DerivationLogConfig {
|
|
567
|
+
/** Log level for derivation debugging. Defaults to 'none'. */
|
|
568
|
+
level: DerivationLogLevel;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Creates the default derivation log configuration.
|
|
572
|
+
*
|
|
573
|
+
* @returns Default DerivationLogConfig
|
|
574
|
+
*/
|
|
575
|
+
declare function createDefaultDerivationLogConfig(): DerivationLogConfig;
|
|
576
|
+
/**
|
|
577
|
+
* Checks if logging should occur at the specified level.
|
|
578
|
+
*
|
|
579
|
+
* @param config - Current log configuration
|
|
580
|
+
* @param minLevel - Minimum level required for logging
|
|
581
|
+
* @returns True if logging should occur
|
|
582
|
+
*/
|
|
583
|
+
declare function shouldLog(config: DerivationLogConfig, minLevel: 'summary' | 'verbose'): boolean;
|
|
584
|
+
/**
|
|
585
|
+
* Type guard to check if a condition is a FormStateCondition.
|
|
586
|
+
*
|
|
587
|
+
* @param condition - The condition to check
|
|
588
|
+
* @returns true if the condition is a FormStateCondition
|
|
589
|
+
*/
|
|
590
|
+
declare function isFormStateCondition(condition: StateLogicConfig['condition'] | DerivationLogicConfig['condition']): condition is FormStateCondition;
|
|
591
|
+
/**
|
|
592
|
+
* Type guard to check if a logic config is a StateLogicConfig.
|
|
593
|
+
*
|
|
594
|
+
* @param config - The logic config to check
|
|
595
|
+
* @returns true if the config is for field state logic
|
|
596
|
+
*/
|
|
597
|
+
declare function isStateLogicConfig(config: LogicConfig): config is StateLogicConfig;
|
|
598
|
+
/**
|
|
599
|
+
* Type guard to check if a logic config is a DerivationLogicConfig.
|
|
600
|
+
*
|
|
601
|
+
* @param config - The logic config to check
|
|
602
|
+
* @returns true if the config is for value derivation
|
|
603
|
+
*/
|
|
604
|
+
declare function isDerivationLogicConfig(config: LogicConfig): config is DerivationLogicConfig;
|
|
605
|
+
/**
|
|
606
|
+
* Type guard to check if a derivation config targets a property rather than the field value.
|
|
607
|
+
*
|
|
608
|
+
* @param config - The derivation logic config to check
|
|
609
|
+
* @returns true if the config has a targetProperty (property derivation)
|
|
610
|
+
*/
|
|
611
|
+
declare function hasTargetProperty(config: DerivationLogicConfig): config is DerivationLogicConfig & {
|
|
612
|
+
targetProperty: string;
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Custom validator function signature using Angular's public FieldContext API
|
|
617
|
+
*
|
|
618
|
+
* @template TValue The type of value stored in the field being validated
|
|
619
|
+
*/
|
|
620
|
+
type CustomValidator<TValue = unknown> = (ctx: FieldContext<TValue>, params?: Record<string, unknown>) => ValidationError$1 | ValidationError$1[] | null;
|
|
621
|
+
/**
|
|
622
|
+
* Async custom validator configuration using Angular's resource-based API
|
|
623
|
+
*
|
|
624
|
+
* @template TValue The type of value stored in the field being validated
|
|
625
|
+
* @template TParams The type of params passed to the resource
|
|
626
|
+
* @template TResult The type of result returned by the resource
|
|
627
|
+
*/
|
|
628
|
+
interface AsyncCustomValidator<TValue = unknown, TParams = unknown, TResult = unknown> {
|
|
629
|
+
/**
|
|
630
|
+
* Function that receives field context and returns resource params.
|
|
631
|
+
* Params will be tracked as a signal and trigger resource reload when changed.
|
|
632
|
+
*/
|
|
633
|
+
readonly params: (ctx: FieldContext<TValue>, config?: Record<string, unknown>) => TParams;
|
|
634
|
+
/**
|
|
635
|
+
* Function that creates a ResourceRef from the params signal.
|
|
636
|
+
* The resource will be automatically managed by Angular's lifecycle.
|
|
637
|
+
*/
|
|
638
|
+
readonly factory: (params: Signal<TParams | undefined>) => ResourceRef<TResult | undefined>;
|
|
639
|
+
/**
|
|
640
|
+
* Optional function to map successful resource result to validation errors.
|
|
641
|
+
* Return null if validation passes, or ValidationError/ValidationError[] if validation fails.
|
|
642
|
+
*/
|
|
643
|
+
readonly onSuccess?: (result: TResult, ctx: FieldContext<TValue>) => TreeValidationResult;
|
|
644
|
+
/**
|
|
645
|
+
* Optional error handler for resource errors (network failures, etc.).
|
|
646
|
+
* Return null to ignore the error, or ValidationError to display it to the user.
|
|
647
|
+
*/
|
|
648
|
+
readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
|
|
649
|
+
}
|
|
650
|
+
/** HTTP request configuration for validateHttp API */
|
|
651
|
+
interface HttpResourceRequest {
|
|
652
|
+
url: string;
|
|
653
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
654
|
+
body?: unknown;
|
|
655
|
+
headers?: Record<string, string | string[]>;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* HTTP-based validator configuration for Angular's validateHttp API
|
|
659
|
+
*
|
|
660
|
+
* @template TValue The type of value stored in the field being validated
|
|
661
|
+
* @template TResult The type of HTTP response
|
|
662
|
+
*/
|
|
663
|
+
interface HttpCustomValidator<TValue = unknown, TResult = unknown> {
|
|
664
|
+
/**
|
|
665
|
+
* Function that returns the HTTP request configuration.
|
|
666
|
+
* Can return:
|
|
667
|
+
* - `undefined` to skip validation (e.g., if field is empty)
|
|
668
|
+
* - `string` for simple GET requests (just the URL)
|
|
669
|
+
* - `HttpResourceRequest` for full control (method, body, headers)
|
|
670
|
+
*/
|
|
671
|
+
readonly request: (ctx: FieldContext<TValue>, config?: Record<string, unknown>) => string | HttpResourceRequest | undefined;
|
|
672
|
+
/** REQUIRED function to map successful HTTP response to validation errors. */
|
|
673
|
+
readonly onSuccess: (result: TResult, ctx: FieldContext<TValue>) => TreeValidationResult;
|
|
674
|
+
/**
|
|
675
|
+
* Optional error handler for HTTP errors (network failures, 4xx/5xx status codes).
|
|
676
|
+
* Return null to ignore the error, or ValidationError to display it to the user.
|
|
677
|
+
*/
|
|
678
|
+
readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/** Mapping configuration for interpreting HTTP responses as validation results. */
|
|
682
|
+
interface HttpValidationResponseMapping {
|
|
683
|
+
/** Expression evaluated with scope `{ response }`. Must evaluate to `true` (strict boolean) to be valid. */
|
|
684
|
+
validWhen: string;
|
|
685
|
+
/** Error kind returned when validation fails — maps to field-level `validationMessages` */
|
|
686
|
+
errorKind: string;
|
|
687
|
+
/**
|
|
688
|
+
* Parameters to include in the validation error for message interpolation.
|
|
689
|
+
* Map of parameter names to expressions evaluated against `{ response }`.
|
|
690
|
+
*/
|
|
691
|
+
errorParams?: Record<string, string>;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/** Base configuration shared by all validators */
|
|
695
|
+
interface BaseValidatorConfig {
|
|
696
|
+
/** Conditional logic for when validator applies */
|
|
697
|
+
when?: ConditionalExpression;
|
|
698
|
+
}
|
|
699
|
+
/** Built-in validator configuration (required, email, min, max, etc.) */
|
|
700
|
+
interface BuiltInValidatorConfig extends BaseValidatorConfig {
|
|
701
|
+
/** Validator type identifier */
|
|
702
|
+
type: 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern';
|
|
703
|
+
/** Static value for the validator (e.g., min value, pattern) */
|
|
704
|
+
value?: number | string | RegExp;
|
|
705
|
+
/** Dynamic value expression that evaluates to validator parameter */
|
|
706
|
+
expression?: string;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Custom validator configuration using Angular's public FieldContext API.
|
|
710
|
+
* Returns ValidationError | ValidationError[] | null synchronously.
|
|
711
|
+
*/
|
|
712
|
+
interface CustomValidatorConfig extends BaseValidatorConfig {
|
|
713
|
+
/** Validator type identifier */
|
|
714
|
+
type: 'custom';
|
|
715
|
+
/** Name of registered validator function (function-based pattern) */
|
|
716
|
+
functionName?: string;
|
|
717
|
+
/**
|
|
718
|
+
* Inline custom validator (code-only authoring).
|
|
719
|
+
* Mutually exclusive with `functionName`. NOT JSON-serializable.
|
|
720
|
+
*/
|
|
721
|
+
fn?: CustomValidator;
|
|
722
|
+
/** Optional parameters to pass to validator function */
|
|
723
|
+
params?: Record<string, unknown>;
|
|
724
|
+
/** JavaScript expression to evaluate (expression-based pattern) */
|
|
725
|
+
expression?: string;
|
|
726
|
+
/** Error kind for expression-based validators - links to validationMessages */
|
|
727
|
+
kind?: string;
|
|
728
|
+
/**
|
|
729
|
+
* Parameters to include in error object for message interpolation (expression-based pattern)
|
|
730
|
+
* Map of parameter names to expressions that will be evaluated in the same context as the validation expression
|
|
731
|
+
*/
|
|
732
|
+
errorParams?: Record<string, string>;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Shared fields for both branches of {@link AsyncValidatorConfig}.
|
|
736
|
+
*
|
|
737
|
+
* @internal
|
|
738
|
+
*/
|
|
739
|
+
interface AsyncValidatorConfigShared extends BaseValidatorConfig {
|
|
740
|
+
/** Validator type identifier. */
|
|
741
|
+
type: 'async';
|
|
742
|
+
/** Optional parameters to pass to validator function */
|
|
743
|
+
params?: Record<string, unknown>;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Async custom validator configuration using Angular's `validateAsync` API.
|
|
747
|
+
* Returns Observable<ValidationError | ValidationError[] | null>.
|
|
748
|
+
*/
|
|
749
|
+
type AsyncValidatorConfig = (AsyncValidatorConfigShared & {
|
|
750
|
+
/** Name of registered async validator function */
|
|
751
|
+
functionName: string;
|
|
752
|
+
/** Inline form is forbidden when `functionName` is set */
|
|
753
|
+
fn?: never;
|
|
754
|
+
}) | (AsyncValidatorConfigShared & {
|
|
755
|
+
/** Inline async validator. Mutually exclusive with `functionName`. */
|
|
756
|
+
fn: AsyncCustomValidator;
|
|
757
|
+
/** Registered form is forbidden when `fn` is set */
|
|
758
|
+
functionName?: never;
|
|
759
|
+
});
|
|
760
|
+
/**
|
|
761
|
+
* Shared fields for both branches of {@link FunctionHttpValidatorConfig}.
|
|
762
|
+
*
|
|
763
|
+
* @internal
|
|
764
|
+
*/
|
|
765
|
+
interface FunctionHttpValidatorConfigShared extends BaseValidatorConfig {
|
|
766
|
+
/** Validator type identifier. */
|
|
767
|
+
type: 'http';
|
|
768
|
+
/** Optional parameters to pass to HTTP validator */
|
|
769
|
+
params?: Record<string, unknown>;
|
|
770
|
+
}
|
|
771
|
+
/** Function-based HTTP validator configuration. Uses Angular's `validateHttp` API. */
|
|
772
|
+
type FunctionHttpValidatorConfig = (FunctionHttpValidatorConfigShared & {
|
|
773
|
+
/** Name of registered HTTP validator configuration */
|
|
774
|
+
functionName: string;
|
|
775
|
+
/** Inline form is forbidden when `functionName` is set */
|
|
776
|
+
fn?: never;
|
|
777
|
+
}) | (FunctionHttpValidatorConfigShared & {
|
|
778
|
+
/** Inline HTTP validator. Mutually exclusive with `functionName`. */
|
|
779
|
+
fn: HttpCustomValidator;
|
|
780
|
+
/** Registered form is forbidden when `fn` is set */
|
|
781
|
+
functionName?: never;
|
|
782
|
+
});
|
|
783
|
+
/** Declarative HTTP validator configuration — fully JSON-serializable, no function registration required. */
|
|
784
|
+
interface DeclarativeHttpValidatorConfig extends BaseValidatorConfig {
|
|
785
|
+
/** Validator type identifier */
|
|
786
|
+
type: 'http';
|
|
787
|
+
/** HTTP request configuration with expression-based query params and body */
|
|
788
|
+
http: HttpRequestConfig;
|
|
789
|
+
/** Mapping that interprets the HTTP response as a validation result */
|
|
790
|
+
responseMapping: HttpValidationResponseMapping;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Configuration for signal forms validator functions that can be serialized from API.
|
|
794
|
+
* Discriminated union type for type-safe validator configuration.
|
|
795
|
+
*/
|
|
796
|
+
type ValidatorConfig = BuiltInValidatorConfig | CustomValidatorConfig | AsyncValidatorConfig | FunctionHttpValidatorConfig | DeclarativeHttpValidatorConfig;
|
|
797
|
+
/** Type guard to distinguish function-based HTTP validators from declarative ones. */
|
|
798
|
+
declare function isFunctionHttpValidator(config: FunctionHttpValidatorConfig | DeclarativeHttpValidatorConfig): config is FunctionHttpValidatorConfig;
|
|
799
|
+
|
|
800
|
+
/** Configuration for applying predefined schemas */
|
|
801
|
+
interface SchemaApplicationConfig {
|
|
802
|
+
/** Schema application type */
|
|
803
|
+
type: 'apply' | 'applyWhen' | 'applyWhenValue' | 'applyEach';
|
|
804
|
+
/** Schema identifier or inline schema definition */
|
|
805
|
+
schema: string | SchemaDefinition;
|
|
806
|
+
/** Condition for conditional application */
|
|
807
|
+
condition?: ConditionalExpression;
|
|
808
|
+
/** Type predicate for applyWhenValue */
|
|
809
|
+
typePredicate?: string;
|
|
810
|
+
}
|
|
811
|
+
/** Reusable schema definition that can be referenced by name */
|
|
812
|
+
interface SchemaDefinition {
|
|
813
|
+
/** Unique schema identifier */
|
|
814
|
+
name: string;
|
|
815
|
+
/** Schema description */
|
|
816
|
+
description?: string;
|
|
817
|
+
/** Field path pattern this schema applies to */
|
|
818
|
+
pathPattern?: string;
|
|
819
|
+
/** Validators to apply */
|
|
820
|
+
validators?: ValidatorConfig[];
|
|
821
|
+
/** Logic rules to apply */
|
|
822
|
+
logic?: LogicConfig[];
|
|
823
|
+
/** Nested schema applications */
|
|
824
|
+
subSchemas?: SchemaApplicationConfig[];
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/** Context for cross-field detection, providing access to function scope information. */
|
|
828
|
+
interface CrossFieldDetectionContext {
|
|
829
|
+
/**
|
|
830
|
+
* Lookup function to get the scope of a registered custom function.
|
|
831
|
+
*
|
|
832
|
+
* @param functionName The name of the custom function
|
|
833
|
+
* @returns The function's scope, or undefined if not registered
|
|
834
|
+
*/
|
|
835
|
+
getFunctionScope?: (functionName: string) => CustomFunctionScope | undefined;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Detects if a ConditionalExpression references other fields (cross-field).
|
|
839
|
+
*
|
|
840
|
+
* @param expr The conditional expression to analyze
|
|
841
|
+
* @param context Optional context providing function scope lookup
|
|
842
|
+
* @returns true if the expression references other fields
|
|
843
|
+
*/
|
|
844
|
+
declare function isCrossFieldExpression(expr: ConditionalExpression | boolean | FormStateCondition | undefined, context?: CrossFieldDetectionContext): boolean;
|
|
845
|
+
/**
|
|
846
|
+
* Extracts field dependencies from a ConditionalExpression.
|
|
847
|
+
*
|
|
848
|
+
* @param expr The conditional expression to analyze
|
|
849
|
+
* @returns Array of field keys that this expression depends on
|
|
850
|
+
*/
|
|
851
|
+
declare function extractExpressionDependencies(expr: ConditionalExpression | boolean | undefined): string[];
|
|
852
|
+
/**
|
|
853
|
+
* Extracts field names from a JavaScript expression string.
|
|
854
|
+
* Handles both dot notation (formValue.field) and bracket notation (formValue['field']).
|
|
855
|
+
*/
|
|
856
|
+
declare function extractStringDependencies(expression: string): string[];
|
|
857
|
+
/**
|
|
858
|
+
* Detects if a custom validator configuration references formValue (cross-field validation).
|
|
859
|
+
*
|
|
860
|
+
* @param config The custom validator configuration to check
|
|
861
|
+
* @returns true if the validator references formValue
|
|
862
|
+
*/
|
|
863
|
+
declare function isCrossFieldValidator(config: CustomValidatorConfig): boolean;
|
|
864
|
+
/**
|
|
865
|
+
* Detects if a built-in validator has a dynamic expression that references formValue.
|
|
866
|
+
*
|
|
867
|
+
* @param config The validator configuration to check
|
|
868
|
+
* @returns true if the validator has a cross-field dynamic expression
|
|
869
|
+
*/
|
|
870
|
+
declare function isCrossFieldBuiltInValidator(config: ValidatorConfig): boolean;
|
|
871
|
+
/** Checks if a validator uses Angular's resource API (validateHttp / validateAsync). */
|
|
872
|
+
declare function isResourceBasedValidator(config: ValidatorConfig): boolean;
|
|
873
|
+
/**
|
|
874
|
+
* Detects if a validator's `when` condition is cross-field.
|
|
875
|
+
*
|
|
876
|
+
* @param config The validator configuration to check
|
|
877
|
+
* @param context Optional context providing function scope lookup
|
|
878
|
+
* @returns true if the when condition references other fields
|
|
879
|
+
*/
|
|
880
|
+
declare function hasCrossFieldWhenCondition(config: ValidatorConfig, context?: CrossFieldDetectionContext): boolean;
|
|
881
|
+
/**
|
|
882
|
+
* Detects if a StateLogicConfig has a cross-field condition.
|
|
883
|
+
*
|
|
884
|
+
* @param config The state logic configuration to check
|
|
885
|
+
* @param context Optional context providing function scope lookup
|
|
886
|
+
* @returns true if the logic condition references other fields
|
|
887
|
+
*/
|
|
888
|
+
declare function isCrossFieldStateLogic(config: StateLogicConfig, context?: CrossFieldDetectionContext): boolean;
|
|
889
|
+
/**
|
|
890
|
+
* Detects if a SchemaApplicationConfig has a cross-field condition.
|
|
891
|
+
*
|
|
892
|
+
* @param config The schema application configuration to check
|
|
893
|
+
* @param context Optional context providing function scope lookup
|
|
894
|
+
* @returns true if the schema condition references other fields
|
|
895
|
+
*/
|
|
896
|
+
declare function isCrossFieldSchema(config: SchemaApplicationConfig, context?: CrossFieldDetectionContext): boolean;
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Creates a lazy FieldStateInfo that only reads state properties when accessed.
|
|
900
|
+
*
|
|
901
|
+
* @param fieldSource - A FieldTree or direct FieldState instance
|
|
902
|
+
* @param reactive - If true, reads signals reactively (creates dependencies).
|
|
903
|
+
* If false, reads signals with `untracked()` (no dependencies).
|
|
904
|
+
* @returns A FieldStateInfo proxy with lazy property access, or undefined if the source is invalid
|
|
905
|
+
* @internal
|
|
906
|
+
*/
|
|
907
|
+
declare function readFieldStateInfo(fieldSource: FieldTree<unknown> | FieldState<unknown> | undefined, reactive: boolean): FieldStateInfo | undefined;
|
|
908
|
+
/**
|
|
909
|
+
* Creates a Proxy-based FormFieldStateMap for accessing any field's state by key.
|
|
910
|
+
*
|
|
911
|
+
* @param rootForm - The root FieldTree of the form
|
|
912
|
+
* @param reactive - If true, reads signals reactively (creates dependencies).
|
|
913
|
+
* If false, reads signals with `untracked()` (no dependencies).
|
|
914
|
+
* @returns A FormFieldStateMap proxy
|
|
915
|
+
* @internal
|
|
916
|
+
*/
|
|
917
|
+
declare function createFormFieldStateMap(rootForm: FieldTree<unknown>, reactive: boolean): FormFieldStateMap;
|
|
918
|
+
|
|
919
|
+
/** DI-scoped cache for async condition logic functions. */
|
|
920
|
+
declare class AsyncConditionFunctionCacheService {
|
|
921
|
+
/** Cache for async condition logic functions, keyed by serialized condition. */
|
|
922
|
+
readonly asyncConditionFunctionCache: Map<string, LogicFn<unknown, boolean>>;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/** Creates a logic function for an async condition. */
|
|
926
|
+
declare function createAsyncConditionLogicFunction<TValue>(condition: AsyncCondition): LogicFn<TValue, boolean>;
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Evaluate conditional expression
|
|
930
|
+
* Uses secure AST-based parsing for JavaScript expressions
|
|
931
|
+
*/
|
|
932
|
+
declare function evaluateCondition(expression: ConditionalExpression, context: EvaluationContext): boolean;
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Trigger value carried through the debounced reactive pipeline.
|
|
936
|
+
*
|
|
937
|
+
* @internal
|
|
938
|
+
*/
|
|
939
|
+
interface Trigger<TPayload> {
|
|
940
|
+
key: string;
|
|
941
|
+
payload: TPayload;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Configuration passed to {@link createDebouncedResourceLogicFn}.
|
|
945
|
+
*
|
|
946
|
+
* @internal
|
|
947
|
+
*/
|
|
948
|
+
interface DebouncedResourceLogicFnConfig<TValue, TPayload, TResult> {
|
|
949
|
+
/** Default value returned by the resource while loading / pending. */
|
|
950
|
+
pendingValue: TResult;
|
|
951
|
+
/** Debounce window (ms) applied to the trigger signal before the resource sees it. */
|
|
952
|
+
debounceMs: number;
|
|
953
|
+
injector: Injector;
|
|
954
|
+
/**
|
|
955
|
+
* Builds the per-emission inner observable. Called every time the debounced
|
|
956
|
+
* trigger key changes; the resource swaps to the new stream automatically.
|
|
957
|
+
*/
|
|
958
|
+
buildStream: (payload: TPayload) => Observable<TResult>;
|
|
959
|
+
/**
|
|
960
|
+
* Called per LogicFn invocation. Returns one of:
|
|
961
|
+
* - `{ kind: 'trigger', key, payload }` — set the trigger to this and
|
|
962
|
+
* return the (potentially still-loading) resource value.
|
|
963
|
+
* - `{ kind: 'returnEarly', value }` — bypass the resource and return
|
|
964
|
+
* this value directly (used by HTTP for response-cache hits — the
|
|
965
|
+
* cached value is authoritative and overrides any sticky resource value).
|
|
966
|
+
* - `{ kind: 'holdPrevious' }` — bypass the resource update but return
|
|
967
|
+
* the resource's current sticky value (its last resolved value via
|
|
968
|
+
* `withPreviousValue`, or `pendingValue` if it never resolved). Used
|
|
969
|
+
* by HTTP when the request template is temporarily unresolvable so
|
|
970
|
+
* the UI doesn't flicker back to `pendingValue`.
|
|
971
|
+
*/
|
|
972
|
+
resolve: (ctx: FieldContext<TValue>) => {
|
|
973
|
+
kind: 'trigger';
|
|
974
|
+
key: string;
|
|
975
|
+
payload: TPayload;
|
|
976
|
+
} | {
|
|
977
|
+
kind: 'returnEarly';
|
|
978
|
+
value: TResult;
|
|
979
|
+
} | {
|
|
980
|
+
kind: 'holdPrevious';
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Shared scaffolding for both async-function and HTTP condition LogicFns.
|
|
985
|
+
*
|
|
986
|
+
* @internal
|
|
987
|
+
*/
|
|
988
|
+
declare function createDebouncedResourceLogicFn<TValue, TPayload, TResult>(config: DebouncedResourceLogicFnConfig<TValue, TPayload, TResult>): LogicFn<TValue, TResult>;
|
|
989
|
+
|
|
990
|
+
/** DI-scoped cache for HTTP condition logic functions. */
|
|
991
|
+
declare class HttpConditionFunctionCacheService {
|
|
992
|
+
/** Cache for HTTP condition logic functions, keyed by serialized condition. */
|
|
993
|
+
readonly httpConditionFunctionCache: Map<string, LogicFn<unknown, boolean>>;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/** Creates a logic function for an HTTP condition. */
|
|
997
|
+
declare function createHttpConditionLogicFunction<TValue>(condition: HttpCondition): LogicFn<TValue, boolean>;
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Create a logic function from a conditional expression.
|
|
1001
|
+
*
|
|
1002
|
+
* @param expression The conditional expression to evaluate
|
|
1003
|
+
* @returns A LogicFn that evaluates the condition in the context of a field
|
|
1004
|
+
*/
|
|
1005
|
+
declare function createLogicFunction<TValue>(expression: ConditionalExpression): LogicFn<TValue, boolean>;
|
|
1006
|
+
/**
|
|
1007
|
+
* Create a debounced logic function from a conditional expression.
|
|
1008
|
+
*
|
|
1009
|
+
* @param expression The conditional expression to evaluate
|
|
1010
|
+
* @param debounceMs The debounce duration in milliseconds
|
|
1011
|
+
* @returns A LogicFn that evaluates the condition with debouncing
|
|
1012
|
+
*/
|
|
1013
|
+
declare function createDebouncedLogicFunction<TValue>(expression: ConditionalExpression, debounceMs: number): LogicFn<TValue, boolean>;
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Compares two values using the specified comparison operator.
|
|
1017
|
+
*
|
|
1018
|
+
* @param actual - The value to compare
|
|
1019
|
+
* @param expected - The value to compare against
|
|
1020
|
+
* @param operator - Comparison operator to use
|
|
1021
|
+
* @returns True if the comparison succeeds, false otherwise
|
|
1022
|
+
*/
|
|
1023
|
+
declare function compareValues(actual: unknown, expected: unknown, operator: string): boolean;
|
|
1024
|
+
/**
|
|
1025
|
+
* Retrieves a nested value from an object using dot notation path.
|
|
1026
|
+
*
|
|
1027
|
+
* @param obj - The object to traverse
|
|
1028
|
+
* @param path - Dot-separated path to the desired property
|
|
1029
|
+
* @returns The value at the specified path, or undefined if not found
|
|
1030
|
+
*/
|
|
1031
|
+
declare function getNestedValue(obj: unknown, path: string): unknown;
|
|
1032
|
+
/**
|
|
1033
|
+
* Checks whether a nested property exists in an object using dot notation path.
|
|
1034
|
+
*
|
|
1035
|
+
* @param obj - The object to check
|
|
1036
|
+
* @param path - Dot-separated path to the property
|
|
1037
|
+
* @returns True if the property exists (even if its value is undefined)
|
|
1038
|
+
*/
|
|
1039
|
+
declare function hasNestedProperty(obj: unknown, path: string): boolean;
|
|
1040
|
+
|
|
1041
|
+
/** Per-field debounced signal pair. */
|
|
1042
|
+
interface DebouncedSignalPair {
|
|
1043
|
+
immediateValue: WritableSignal<boolean>;
|
|
1044
|
+
debouncedValue: Signal<boolean | undefined>;
|
|
1045
|
+
}
|
|
1046
|
+
/** DI-scoped cache for logic functions and debounced signals. */
|
|
1047
|
+
declare class LogicFunctionCacheService {
|
|
1048
|
+
/** Cache for memoized logic functions, keyed by serialized expression. */
|
|
1049
|
+
readonly logicFunctionCache: Map<string, LogicFn<unknown, boolean>>;
|
|
1050
|
+
/** Cache for debounced logic functions, keyed by serialized expression + debounceMs. */
|
|
1051
|
+
readonly debouncedLogicFunctionCache: Map<string, LogicFn<unknown, boolean>>;
|
|
1052
|
+
/** Per-field signal store for debounced logic, keyed by field path. */
|
|
1053
|
+
readonly debouncedSignalStore: Map<string, DebouncedSignalPair>;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/** Base error class for all Dynamic Forms errors. */
|
|
1057
|
+
declare class DynamicFormError extends Error {
|
|
1058
|
+
private static readonly PREFIX;
|
|
1059
|
+
constructor(message: string);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/** Token types for expression parsing */
|
|
1063
|
+
declare enum TokenType {
|
|
1064
|
+
NUMBER = "NUMBER",
|
|
1065
|
+
STRING = "STRING",
|
|
1066
|
+
TRUE = "TRUE",
|
|
1067
|
+
FALSE = "FALSE",
|
|
1068
|
+
NULL = "NULL",
|
|
1069
|
+
UNDEFINED = "UNDEFINED",
|
|
1070
|
+
IDENTIFIER = "IDENTIFIER",
|
|
1071
|
+
PLUS = "PLUS",
|
|
1072
|
+
MINUS = "MINUS",
|
|
1073
|
+
MULTIPLY = "MULTIPLY",
|
|
1074
|
+
DIVIDE = "DIVIDE",
|
|
1075
|
+
MODULO = "MODULO",
|
|
1076
|
+
EQUAL = "EQUAL",
|
|
1077
|
+
NOT_EQUAL = "NOT_EQUAL",
|
|
1078
|
+
GREATER = "GREATER",
|
|
1079
|
+
LESS = "LESS",
|
|
1080
|
+
GREATER_EQUAL = "GREATER_EQUAL",
|
|
1081
|
+
LESS_EQUAL = "LESS_EQUAL",
|
|
1082
|
+
AND = "AND",
|
|
1083
|
+
OR = "OR",
|
|
1084
|
+
NOT = "NOT",
|
|
1085
|
+
DOT = "DOT",
|
|
1086
|
+
COMMA = "COMMA",
|
|
1087
|
+
COLON = "COLON",
|
|
1088
|
+
LPAREN = "LPAREN",
|
|
1089
|
+
RPAREN = "RPAREN",
|
|
1090
|
+
LBRACKET = "LBRACKET",
|
|
1091
|
+
RBRACKET = "RBRACKET",
|
|
1092
|
+
LBRACE = "LBRACE",
|
|
1093
|
+
RBRACE = "RBRACE",
|
|
1094
|
+
ARROW = "ARROW",
|
|
1095
|
+
QUESTION = "QUESTION",
|
|
1096
|
+
OPTIONAL_DOT = "OPTIONAL_DOT",
|
|
1097
|
+
EOF = "EOF"
|
|
1098
|
+
}
|
|
1099
|
+
/** Token with type, value, and position */
|
|
1100
|
+
interface Token {
|
|
1101
|
+
type: TokenType;
|
|
1102
|
+
value: string;
|
|
1103
|
+
position: number;
|
|
1104
|
+
}
|
|
1105
|
+
/** AST Node types */
|
|
1106
|
+
type ASTNode = LiteralNode | IdentifierNode | MemberAccessNode | ComputedMemberAccessNode | BinaryOpNode | UnaryOpNode | CallExpressionNode | ConditionalNode | ArrayLiteralNode | ObjectLiteralNode | ArrowFunctionNode;
|
|
1107
|
+
interface LiteralNode {
|
|
1108
|
+
type: 'Literal';
|
|
1109
|
+
value: string | number | boolean | null | undefined;
|
|
1110
|
+
}
|
|
1111
|
+
interface IdentifierNode {
|
|
1112
|
+
type: 'Identifier';
|
|
1113
|
+
name: string;
|
|
1114
|
+
}
|
|
1115
|
+
interface MemberAccessNode {
|
|
1116
|
+
type: 'MemberAccess';
|
|
1117
|
+
object: ASTNode;
|
|
1118
|
+
property: string;
|
|
1119
|
+
/** True when this access used `?.` (optional chaining). */
|
|
1120
|
+
optional?: boolean;
|
|
1121
|
+
}
|
|
1122
|
+
interface ComputedMemberAccessNode {
|
|
1123
|
+
type: 'ComputedMemberAccess';
|
|
1124
|
+
object: ASTNode;
|
|
1125
|
+
/** Expression evaluated at runtime to produce the property key. */
|
|
1126
|
+
property: ASTNode;
|
|
1127
|
+
/** True when this access used `?.[ ... ]` (optional chaining). */
|
|
1128
|
+
optional?: boolean;
|
|
1129
|
+
}
|
|
1130
|
+
interface ConditionalNode {
|
|
1131
|
+
type: 'Conditional';
|
|
1132
|
+
test: ASTNode;
|
|
1133
|
+
consequent: ASTNode;
|
|
1134
|
+
alternate: ASTNode;
|
|
1135
|
+
}
|
|
1136
|
+
interface BinaryOpNode {
|
|
1137
|
+
type: 'BinaryOp';
|
|
1138
|
+
operator: string;
|
|
1139
|
+
left: ASTNode;
|
|
1140
|
+
right: ASTNode;
|
|
1141
|
+
}
|
|
1142
|
+
interface UnaryOpNode {
|
|
1143
|
+
type: 'UnaryOp';
|
|
1144
|
+
operator: string;
|
|
1145
|
+
operand: ASTNode;
|
|
1146
|
+
}
|
|
1147
|
+
interface CallExpressionNode {
|
|
1148
|
+
type: 'CallExpression';
|
|
1149
|
+
callee: ASTNode;
|
|
1150
|
+
arguments: ASTNode[];
|
|
1151
|
+
/** True when this call used `?.( ... )` (optional chaining). */
|
|
1152
|
+
optional?: boolean;
|
|
1153
|
+
}
|
|
1154
|
+
interface ArrayLiteralNode {
|
|
1155
|
+
type: 'ArrayLiteral';
|
|
1156
|
+
elements: ASTNode[];
|
|
1157
|
+
}
|
|
1158
|
+
interface ObjectLiteralNode {
|
|
1159
|
+
type: 'ObjectLiteral';
|
|
1160
|
+
properties: Array<{
|
|
1161
|
+
key: string;
|
|
1162
|
+
value: ASTNode;
|
|
1163
|
+
}>;
|
|
1164
|
+
}
|
|
1165
|
+
interface ArrowFunctionNode {
|
|
1166
|
+
type: 'ArrowFunction';
|
|
1167
|
+
params: string[];
|
|
1168
|
+
body: ASTNode;
|
|
1169
|
+
}
|
|
1170
|
+
/** Parser error with position information */
|
|
1171
|
+
declare class ExpressionParserError extends DynamicFormError {
|
|
1172
|
+
position: number;
|
|
1173
|
+
expression: string;
|
|
1174
|
+
constructor(message: string, position: number, expression: string);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/** Evaluation context for safe expression evaluation */
|
|
1178
|
+
interface EvaluationScope {
|
|
1179
|
+
[key: string]: unknown;
|
|
1180
|
+
}
|
|
1181
|
+
/** Safely evaluates an AST node with a given context */
|
|
1182
|
+
declare class Evaluator {
|
|
1183
|
+
private readonly scope;
|
|
1184
|
+
private readonly expression;
|
|
1185
|
+
constructor(scope: EvaluationScope, expression: string);
|
|
1186
|
+
/** Evaluate an AST node */
|
|
1187
|
+
evaluate(node: ASTNode): unknown;
|
|
1188
|
+
private evaluateIdentifier;
|
|
1189
|
+
private evaluateMemberAccess;
|
|
1190
|
+
private evaluateComputedMemberAccess;
|
|
1191
|
+
private evaluateBinaryOp;
|
|
1192
|
+
private evaluateUnaryOp;
|
|
1193
|
+
private evaluateCallExpression;
|
|
1194
|
+
private evaluateArrayLiteral;
|
|
1195
|
+
private evaluateObjectLiteral;
|
|
1196
|
+
private evaluateArrowFunction;
|
|
1197
|
+
private isMethodSafe;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/** Secure expression parser that uses AST-based evaluation */
|
|
1201
|
+
declare class ExpressionParser {
|
|
1202
|
+
/**
|
|
1203
|
+
* Parse an expression string into an AST
|
|
1204
|
+
* Results are cached for performance using LRU eviction
|
|
1205
|
+
*/
|
|
1206
|
+
static parse(expression: string): ASTNode;
|
|
1207
|
+
/** Evaluate an expression with a given scope */
|
|
1208
|
+
static evaluate(expression: string, scope: EvaluationScope): unknown;
|
|
1209
|
+
/** Clear the AST cache */
|
|
1210
|
+
static clearCache(): void;
|
|
1211
|
+
/** Get cache statistics */
|
|
1212
|
+
static getCacheStats(): {
|
|
1213
|
+
size: number;
|
|
1214
|
+
maxSize: number;
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* Parses tokens into an Abstract Syntax Tree (AST)
|
|
1220
|
+
* Uses recursive descent parsing with operator precedence
|
|
1221
|
+
*/
|
|
1222
|
+
declare class Parser {
|
|
1223
|
+
private tokens;
|
|
1224
|
+
private current;
|
|
1225
|
+
private readonly expression;
|
|
1226
|
+
constructor(expression: string);
|
|
1227
|
+
/** Parse the expression into an AST */
|
|
1228
|
+
parse(): ASTNode;
|
|
1229
|
+
private parseExpression;
|
|
1230
|
+
/**
|
|
1231
|
+
* Ternary `test ? consequent : alternate` — right-associative, sits between
|
|
1232
|
+
* the arrow-function check and logical-or in precedence.
|
|
1233
|
+
*/
|
|
1234
|
+
private parseConditional;
|
|
1235
|
+
/**
|
|
1236
|
+
* Attempts to parse an arrow function. Returns null and leaves position
|
|
1237
|
+
* unchanged if the next tokens don't form an arrow function head.
|
|
1238
|
+
*/
|
|
1239
|
+
private tryParseArrowFunction;
|
|
1240
|
+
private checkAt;
|
|
1241
|
+
private parseLogicalOr;
|
|
1242
|
+
private parseLogicalAnd;
|
|
1243
|
+
private parseEquality;
|
|
1244
|
+
private parseComparison;
|
|
1245
|
+
private parseAddition;
|
|
1246
|
+
private parseMultiplication;
|
|
1247
|
+
private parseUnary;
|
|
1248
|
+
private parsePostfix;
|
|
1249
|
+
private parsePrimary;
|
|
1250
|
+
private parseArgumentList;
|
|
1251
|
+
private parseArrayElements;
|
|
1252
|
+
private parseObjectProperties;
|
|
1253
|
+
private match;
|
|
1254
|
+
private check;
|
|
1255
|
+
private advance;
|
|
1256
|
+
private isAtEnd;
|
|
1257
|
+
private peek;
|
|
1258
|
+
private previous;
|
|
1259
|
+
private consume;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/** Tokenizes an expression string into tokens */
|
|
1263
|
+
declare class Tokenizer {
|
|
1264
|
+
private position;
|
|
1265
|
+
private readonly expression;
|
|
1266
|
+
constructor(expression: string);
|
|
1267
|
+
/** Tokenize the entire expression */
|
|
1268
|
+
tokenize(): Token[];
|
|
1269
|
+
private skipWhitespace;
|
|
1270
|
+
private nextToken;
|
|
1271
|
+
private readString;
|
|
1272
|
+
private readNumber;
|
|
1273
|
+
private readIdentifierOrKeyword;
|
|
1274
|
+
private getKeywordType;
|
|
1275
|
+
private readOperatorOrPunctuation;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/** Represents a FieldTree node for dynamic bracket-access navigation. */
|
|
1279
|
+
type FieldTreeRecord = Record<string, FieldTree<unknown> | undefined>;
|
|
1280
|
+
/**
|
|
1281
|
+
* Type representing a FieldTree for an array.
|
|
1282
|
+
* Extends FieldTree to add numeric indexing for accessing item FieldTrees.
|
|
1283
|
+
*/
|
|
1284
|
+
type ArrayFieldTree<T> = FieldTree<T[]> & {
|
|
1285
|
+
readonly [index: number]: FieldTree<T> | undefined;
|
|
1286
|
+
};
|
|
1287
|
+
/** Get the length of an array FieldTree. */
|
|
1288
|
+
declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
|
|
1289
|
+
/** Read-only view of a single field's observable state. */
|
|
1290
|
+
interface ReadonlyFieldTree<TValue = unknown> {
|
|
1291
|
+
readonly value: Signal<TValue>;
|
|
1292
|
+
readonly valid: Signal<boolean>;
|
|
1293
|
+
readonly invalid: Signal<boolean>;
|
|
1294
|
+
readonly touched: Signal<boolean>;
|
|
1295
|
+
readonly dirty: Signal<boolean>;
|
|
1296
|
+
readonly required: Signal<boolean>;
|
|
1297
|
+
readonly disabled: Signal<boolean>;
|
|
1298
|
+
readonly hidden: Signal<boolean>;
|
|
1299
|
+
readonly errors: Signal<readonly unknown[]>;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
|
|
1303
|
+
* Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
|
|
1304
|
+
* so consumers only see the narrow surface and Angular's `WritableSignal` capability
|
|
1305
|
+
* on `value` is hidden.
|
|
1306
|
+
*/
|
|
1307
|
+
declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
|
|
1308
|
+
/**
|
|
1309
|
+
* Write a value into a FieldTree's underlying signal. Signal Forms stores
|
|
1310
|
+
* `FieldState.value` as a `WritableSignal`; this helper centralises the cast
|
|
1311
|
+
* and warns if that contract ever changes (vs a silent no-op).
|
|
1312
|
+
*/
|
|
1313
|
+
declare function writeToFieldValue<T>(value: Signal<T>, next: T, logger?: {
|
|
1314
|
+
warn: (msg: string) => void;
|
|
1315
|
+
}): boolean;
|
|
1316
|
+
/**
|
|
1317
|
+
* DI-scoped cache for `ReadonlyFieldTree` views, keyed on the source FieldTree
|
|
1318
|
+
* identity. A fresh WeakMap per root injector keeps renders isolated under SSR —
|
|
1319
|
+
* Angular creates a new root injector per request, so there's no shared state
|
|
1320
|
+
* between server renders (same pattern as `COMPONENT_CACHE`).
|
|
1321
|
+
*
|
|
1322
|
+
* @internal
|
|
1323
|
+
*/
|
|
1324
|
+
declare const READONLY_FIELD_TREE_CACHE: InjectionToken<WeakMap<FieldTree<unknown>, ReadonlyFieldTree<unknown>>>;
|
|
1325
|
+
/**
|
|
1326
|
+
* Cached variant of {@link toReadonlyFieldTree}. Each FieldTree instance is a
|
|
1327
|
+
* stable reference for the lifetime of its form, so one `ReadonlyFieldTree`
|
|
1328
|
+
* view per tree is enough. Callers on hot paths (`pushInputs` on every mapper
|
|
1329
|
+
* emission) avoid re-allocating the nine-property view object.
|
|
1330
|
+
*
|
|
1331
|
+
* @internal
|
|
1332
|
+
*/
|
|
1333
|
+
declare function toReadonlyFieldTreeCached<TValue>(cache: WeakMap<FieldTree<unknown>, ReadonlyFieldTree<unknown>>, field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
|
|
1334
|
+
|
|
1335
|
+
/** TTL cache for HTTP condition responses with max entries eviction. */
|
|
1336
|
+
declare class HttpConditionCache {
|
|
1337
|
+
private readonly cache;
|
|
1338
|
+
private readonly maxEntries;
|
|
1339
|
+
constructor(maxEntries?: number);
|
|
1340
|
+
get(key: string): boolean | undefined;
|
|
1341
|
+
set(key: string, value: boolean, ttlMs: number): void;
|
|
1342
|
+
clear(): void;
|
|
1343
|
+
}
|
|
1344
|
+
/** @internal */
|
|
1345
|
+
declare const HTTP_CONDITION_CACHE: InjectionToken<HttpConditionCache>;
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Resolves an `HttpRequestConfig` into an `HttpResourceRequest` by evaluating
|
|
1349
|
+
* expression-based query params and (optionally) body values.
|
|
1350
|
+
*/
|
|
1351
|
+
declare function resolveHttpRequest(config: HttpRequestConfig, context: EvaluationContext): HttpResourceRequest | null;
|
|
1352
|
+
|
|
1353
|
+
/** Evaluates an HTTP response against a `HttpValidationResponseMapping` to produce a validation result. */
|
|
1354
|
+
declare function evaluateHttpValidationResponse(response: unknown, mapping: HttpValidationResponseMapping, logger: Logger): ValidationError$1 | null;
|
|
1355
|
+
|
|
1356
|
+
type DynamicText = string | Observable<string> | Signal<string>;
|
|
1357
|
+
|
|
1358
|
+
/** Generic reactive value — a static value, an Angular Signal, or an RxJS Observable. */
|
|
1359
|
+
type DynamicValue<T> = T | Signal<T> | Observable<T>;
|
|
1360
|
+
|
|
1361
|
+
/** Universal addon slot names supported by every adapter. */
|
|
1362
|
+
type CommonAddonSlot = 'prefix' | 'suffix';
|
|
1363
|
+
/** Module-augmentable registry of adapter-specific addon slot names. */
|
|
1364
|
+
interface DynamicFormAddonSlotRegistry {
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Union of every valid addon slot — universal slots plus any registered via
|
|
1368
|
+
* {@link DynamicFormAddonSlotRegistry} module augmentation.
|
|
1369
|
+
*/
|
|
1370
|
+
type AddonSlot = CommonAddonSlot | keyof DynamicFormAddonSlotRegistry;
|
|
1371
|
+
|
|
1372
|
+
/** Base shape every addon kind extends. */
|
|
1373
|
+
interface BaseAddon<TSlot extends AddonSlot = AddonSlot> {
|
|
1374
|
+
/** Slot to render this addon in. */
|
|
1375
|
+
readonly slot: TSlot;
|
|
1376
|
+
/** CSS class applied to the addon host element. */
|
|
1377
|
+
readonly className?: string;
|
|
1378
|
+
/** Reactive visibility — when `true`, the addon is removed from layout. */
|
|
1379
|
+
readonly hidden?: DynamicValue<boolean>;
|
|
1380
|
+
/** Reactive disabled state — primarily relevant for interactive kinds. */
|
|
1381
|
+
readonly disabled?: DynamicValue<boolean>;
|
|
1382
|
+
}
|
|
1383
|
+
/** Module-augmentable registry of addon kinds. */
|
|
1384
|
+
interface DynamicFormAddonRegistry {
|
|
1385
|
+
text: TextAddon;
|
|
1386
|
+
template: TemplateAddon;
|
|
1387
|
+
component: ComponentAddon;
|
|
1388
|
+
}
|
|
1389
|
+
/** Union of every registered addon shape. */
|
|
1390
|
+
type AnyAddon = DynamicFormAddonRegistry[keyof DynamicFormAddonRegistry];
|
|
1391
|
+
/** Renders text content inline. */
|
|
1392
|
+
interface TextAddon extends BaseAddon {
|
|
1393
|
+
readonly kind: 'text';
|
|
1394
|
+
readonly text: DynamicText;
|
|
1395
|
+
}
|
|
1396
|
+
/** Renders a named `<ng-template>` projected into the host `<df-dynamic-form>`. */
|
|
1397
|
+
interface TemplateAddon extends BaseAddon {
|
|
1398
|
+
readonly kind: 'template';
|
|
1399
|
+
readonly templateKey: string;
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Renders an arbitrary Angular component as the addon body.
|
|
1403
|
+
*
|
|
1404
|
+
* @codeOnly The `component` loader is a function and cannot survive
|
|
1405
|
+
* `JSON.stringify`/`parse`. The lenient validator drops this kind when the
|
|
1406
|
+
* config originated from JSON (set via `sanitizeFormConfig({ source: 'json' })`).
|
|
1407
|
+
*/
|
|
1408
|
+
interface ComponentAddon extends BaseAddon {
|
|
1409
|
+
readonly kind: 'component';
|
|
1410
|
+
readonly component: () => Promise<Type<unknown> | {
|
|
1411
|
+
default: Type<unknown>;
|
|
1412
|
+
}>;
|
|
1413
|
+
readonly inputs?: Record<string, unknown>;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
type WithInputSignals<T> = {
|
|
1417
|
+
readonly [K in keyof T]-?: Signal<T[K]>;
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
type Prettify<T> = {
|
|
1421
|
+
[K in keyof T]: T[K];
|
|
1422
|
+
} & {};
|
|
1423
|
+
|
|
1424
|
+
/** Resolves a wrapper type name to its registered config interface. */
|
|
1425
|
+
type WrapperConfig<TWrappers extends RegisteredWrapperTypes = RegisteredWrapperTypes> = TWrappers extends keyof FieldRegistryWrappers ? FieldRegistryWrappers[TWrappers] : {
|
|
1426
|
+
readonly type: TWrappers;
|
|
1427
|
+
};
|
|
1428
|
+
/**
|
|
1429
|
+
* Signature of a lazy component loader — either a direct component class or
|
|
1430
|
+
* an ES module whose `default` export is the component. Shared by field and
|
|
1431
|
+
* wrapper registrations so the `loadComponent: () => import('./x.component')`
|
|
1432
|
+
* idiom types cleanly in both places.
|
|
1433
|
+
*/
|
|
1434
|
+
type LazyComponentLoader<T = unknown> = () => Promise<Type<T> | {
|
|
1435
|
+
default: Type<T>;
|
|
1436
|
+
}>;
|
|
1437
|
+
/**
|
|
1438
|
+
* Pre-computed reverse index used by `resolveWrappers`: field type → the
|
|
1439
|
+
* wrappers that auto-apply to it. Built once in the `provideDynamicForm(...)`
|
|
1440
|
+
* factory from every registered `WrapperTypeDefinition.types` entry.
|
|
1441
|
+
*/
|
|
1442
|
+
type WrapperAutoAssociations = ReadonlyMap<string, readonly WrapperConfig[]>;
|
|
1443
|
+
/** Configuration interface for registering wrapper types. */
|
|
1444
|
+
interface WrapperTypeDefinition<T extends WrapperConfig = WrapperConfig> {
|
|
1445
|
+
/** Unique identifier for the wrapper type (also serves as discriminant from FieldTypeDefinition) */
|
|
1446
|
+
wrapperName: string;
|
|
1447
|
+
/** Wrapper definition type marker (internal use) */
|
|
1448
|
+
_wrapper?: T;
|
|
1449
|
+
/**
|
|
1450
|
+
* Function to load the wrapper component (supports lazy loading).
|
|
1451
|
+
* Returns a Promise that resolves to the component class or module with default export.
|
|
1452
|
+
*/
|
|
1453
|
+
loadComponent: LazyComponentLoader;
|
|
1454
|
+
/** Field types this wrapper should auto-apply to. */
|
|
1455
|
+
types?: readonly string[];
|
|
1456
|
+
}
|
|
1457
|
+
/** Type guard for WrapperTypeDefinition. */
|
|
1458
|
+
declare function isWrapperTypeDefinition(value: unknown): value is WrapperTypeDefinition;
|
|
1459
|
+
/**
|
|
1460
|
+
* Contract that wrapper components must satisfy.
|
|
1461
|
+
*
|
|
1462
|
+
* @Component({
|
|
1463
|
+
* template: `
|
|
1464
|
+
* <dbx-section [header]="header() ?? ''">
|
|
1465
|
+
* <ng-container #fieldComponent></ng-container>
|
|
1466
|
+
* </dbx-section>
|
|
1467
|
+
* `,
|
|
1468
|
+
* })
|
|
1469
|
+
* export class SectionWrapperComponent implements FieldWrapper {
|
|
1470
|
+
* readonly fieldComponent = viewChild.required('fieldComponent', { read: ViewContainerRef });
|
|
1471
|
+
* readonly header = input<string>();
|
|
1472
|
+
* readonly fieldInputs = input<WrapperFieldInputs>();
|
|
1473
|
+
* }
|
|
1474
|
+
* ```
|
|
1475
|
+
*/
|
|
1476
|
+
interface FieldWrapper {
|
|
1477
|
+
/** ViewContainerRef slot where inner content is rendered */
|
|
1478
|
+
readonly fieldComponent: Signal<ViewContainerRef>;
|
|
1479
|
+
}
|
|
1480
|
+
/** Injection token for the wrapper type registry. */
|
|
1481
|
+
declare const WRAPPER_REGISTRY: InjectionToken<Map<string, WrapperTypeDefinition<_ng_forge_dynamic_forms_internal.RowWrapper | _ng_forge_dynamic_forms_internal.CssWrapper>>>;
|
|
1482
|
+
/** Component cache for loaded wrapper components. */
|
|
1483
|
+
declare const WRAPPER_COMPONENT_CACHE: InjectionToken<Map<string, Type<unknown>>>;
|
|
1484
|
+
/**
|
|
1485
|
+
* Pre-computed reverse index: `fieldType → WrapperConfig[]` for every
|
|
1486
|
+
* registered `WrapperTypeDefinition.types` entry. Built once in the
|
|
1487
|
+
* `provideDynamicForm(...)` factory so `resolveWrappers` can look up
|
|
1488
|
+
* auto-associations in O(1) per field instead of scanning the full
|
|
1489
|
+
* wrapper registry on every render.
|
|
1490
|
+
*/
|
|
1491
|
+
declare const WRAPPER_AUTO_ASSOCIATIONS: InjectionToken<WrapperAutoAssociations>;
|
|
1492
|
+
|
|
1493
|
+
/** Base interface for native HTML attributes that can be passed to form field elements. */
|
|
1494
|
+
interface FieldMeta {
|
|
1495
|
+
/** Allows any data-* attribute */
|
|
1496
|
+
[key: `data-${string}`]: string | undefined;
|
|
1497
|
+
/** Allows any aria-* attribute */
|
|
1498
|
+
[key: `aria-${string}`]: string | boolean | undefined;
|
|
1499
|
+
/** Allows additional custom attributes as escape hatch */
|
|
1500
|
+
[key: string]: string | number | boolean | undefined;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Base interface for all dynamic form field definitions.
|
|
1505
|
+
*
|
|
1506
|
+
* @typeParam TProps - Field-specific properties interface
|
|
1507
|
+
* @typeParam TMeta - Native HTML attributes interface (extends FieldMeta)
|
|
1508
|
+
*/
|
|
1509
|
+
interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
|
|
1510
|
+
/** Unique field identifier used for form binding and value tracking. */
|
|
1511
|
+
key: string;
|
|
1512
|
+
/** Field type identifier for component selection. */
|
|
1513
|
+
type: RegisteredFieldTypes['type'] | (string & {});
|
|
1514
|
+
/** Human-readable field label displayed to users. */
|
|
1515
|
+
label?: DynamicText;
|
|
1516
|
+
/** Field-specific properties and configuration options. */
|
|
1517
|
+
props?: TProps;
|
|
1518
|
+
/** Native HTML attributes to pass through to the underlying element. */
|
|
1519
|
+
meta?: TMeta;
|
|
1520
|
+
/** Additional CSS classes for custom styling. */
|
|
1521
|
+
className?: string;
|
|
1522
|
+
/**
|
|
1523
|
+
* Whether the field is disabled and cannot be interacted with.
|
|
1524
|
+
*
|
|
1525
|
+
* @value false
|
|
1526
|
+
*/
|
|
1527
|
+
disabled?: boolean;
|
|
1528
|
+
/**
|
|
1529
|
+
* Whether the field is read-only.
|
|
1530
|
+
*
|
|
1531
|
+
* @value false
|
|
1532
|
+
*/
|
|
1533
|
+
readonly?: boolean;
|
|
1534
|
+
/**
|
|
1535
|
+
* Whether the field is hidden from view.
|
|
1536
|
+
*
|
|
1537
|
+
* @value false
|
|
1538
|
+
*/
|
|
1539
|
+
hidden?: boolean;
|
|
1540
|
+
/** Tab index for keyboard navigation. */
|
|
1541
|
+
tabIndex?: number | undefined;
|
|
1542
|
+
/**
|
|
1543
|
+
* Whether to exclude this field's value from submission output when hidden.
|
|
1544
|
+
*
|
|
1545
|
+
* @default undefined (uses form-level or global setting)
|
|
1546
|
+
*/
|
|
1547
|
+
excludeValueIfHidden?: boolean;
|
|
1548
|
+
/**
|
|
1549
|
+
* Whether to exclude this field's value from submission output when disabled.
|
|
1550
|
+
*
|
|
1551
|
+
* @default undefined (uses form-level or global setting)
|
|
1552
|
+
*/
|
|
1553
|
+
excludeValueIfDisabled?: boolean;
|
|
1554
|
+
/**
|
|
1555
|
+
* Whether to exclude this field's value from submission output when readonly.
|
|
1556
|
+
*
|
|
1557
|
+
* @default undefined (uses form-level or global setting)
|
|
1558
|
+
*/
|
|
1559
|
+
excludeValueIfReadonly?: boolean;
|
|
1560
|
+
/**
|
|
1561
|
+
* Whether to run validation when this field is hidden — statically (`hidden: true`),
|
|
1562
|
+
* via a `hidden` logic rule, or via any hidden ancestor.
|
|
1563
|
+
*
|
|
1564
|
+
* @default undefined — inherits from parent / form / global default (which is `false`)
|
|
1565
|
+
*/
|
|
1566
|
+
validateWhenHidden?: boolean;
|
|
1567
|
+
/**
|
|
1568
|
+
* Column sizing configuration for responsive grid layout.
|
|
1569
|
+
*
|
|
1570
|
+
* @value 12
|
|
1571
|
+
*/
|
|
1572
|
+
col?: number;
|
|
1573
|
+
/** Wrapper components to chain around this field. */
|
|
1574
|
+
wrappers?: readonly WrapperConfig[] | null;
|
|
1575
|
+
/**
|
|
1576
|
+
* Skip the auto-association layer (`WrapperTypeDefinition.types`) while
|
|
1577
|
+
* keeping form-level defaults and any field-level `wrappers`. Use when a
|
|
1578
|
+
* global wrapper auto-applies to this field type but isn't wanted here.
|
|
1579
|
+
*/
|
|
1580
|
+
skipAutoWrappers?: boolean;
|
|
1581
|
+
/**
|
|
1582
|
+
* Skip the form-level `defaultWrappers` layer while keeping auto-associations
|
|
1583
|
+
* and any field-level `wrappers`.
|
|
1584
|
+
*/
|
|
1585
|
+
skipDefaultWrappers?: boolean;
|
|
1586
|
+
/** Addons rendered inside this field's slots (typically `prefix` / `suffix`). */
|
|
1587
|
+
addons?: ReadonlyArray<AnyAddon>;
|
|
1588
|
+
}
|
|
1589
|
+
type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
|
|
1590
|
+
/**
|
|
1591
|
+
* Type utility for extracting component input properties from field definitions.
|
|
1592
|
+
*
|
|
1593
|
+
* @Component({...})
|
|
1594
|
+
* export class MyFieldComponent implements FieldComponent<MyFieldDef> {
|
|
1595
|
+
* label = input<string>();
|
|
1596
|
+
* className = input<string>();
|
|
1597
|
+
* hidden = input<boolean>();
|
|
1598
|
+
* tabIndex = input<number>();
|
|
1599
|
+
* }
|
|
1600
|
+
* ```
|
|
1601
|
+
* @typeParam T - Field definition type to extract properties from
|
|
1602
|
+
*/
|
|
1603
|
+
type FieldComponent<T extends FieldDef<unknown, FieldMeta>> = Prettify<WithInputSignals<Pick<T, IncludedKeys>>>;
|
|
1604
|
+
|
|
1605
|
+
/**
|
|
1606
|
+
* Logic configuration for container fields (page, group, row, array).
|
|
1607
|
+
* Containers only support the 'hidden' logic type since they are layout containers,
|
|
1608
|
+
* not form controls (they can't be readonly, disabled, or required).
|
|
1609
|
+
*/
|
|
1610
|
+
interface ContainerLogicConfig {
|
|
1611
|
+
/** Only 'hidden' is supported for container fields */
|
|
1612
|
+
type: 'hidden';
|
|
1613
|
+
/**
|
|
1614
|
+
* Condition that determines when this container is hidden.
|
|
1615
|
+
* Can be a boolean or a conditional expression evaluated against form values.
|
|
1616
|
+
*/
|
|
1617
|
+
condition: ConditionalExpression | boolean;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/**
|
|
1621
|
+
* Row field interface for creating horizontal layouts.
|
|
1622
|
+
* A row is a synthetic field type that resolves to a Container at runtime,
|
|
1623
|
+
* with a synthesized `{ type: 'row' }` wrapper applied for layout. The row
|
|
1624
|
+
* itself does not hold a value — its children flatten into the parent form.
|
|
1625
|
+
*/
|
|
1626
|
+
interface RowField<TFields extends readonly RowAllowedChildren[] = readonly RowAllowedChildren[]> extends FieldDef<never> {
|
|
1627
|
+
type: 'row';
|
|
1628
|
+
/** Child definitions to render within this row */
|
|
1629
|
+
readonly fields: TFields;
|
|
1630
|
+
/** Row fields do not have a label property * */
|
|
1631
|
+
readonly label?: never;
|
|
1632
|
+
/** Rows do not support meta - they have no native form element * */
|
|
1633
|
+
readonly meta?: never;
|
|
1634
|
+
/**
|
|
1635
|
+
* Logic configurations for conditional row visibility.
|
|
1636
|
+
* Only 'hidden' type logic is supported for rows.
|
|
1637
|
+
*/
|
|
1638
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1639
|
+
}
|
|
1640
|
+
/** Type guard for RowField with proper type narrowing */
|
|
1641
|
+
declare function isRowField(field: FieldDef<any>): field is RowField;
|
|
1642
|
+
type RowComponent = FieldComponent<RowField<RowAllowedChildren[]>>;
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* Group field interface for creating logical field groupings that map to object values
|
|
1646
|
+
* Groups create nested form structures where child field values are collected into an object
|
|
1647
|
+
* This is a programmatic grouping only - users cannot customize this field type
|
|
1648
|
+
*/
|
|
1649
|
+
interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never> {
|
|
1650
|
+
type: 'group';
|
|
1651
|
+
readonly fields: TFields;
|
|
1652
|
+
/** Groups do not have a label property - they are logical containers only * */
|
|
1653
|
+
readonly label?: never;
|
|
1654
|
+
/** Groups do not support meta - they have no native form element * */
|
|
1655
|
+
readonly meta?: never;
|
|
1656
|
+
/**
|
|
1657
|
+
* Logic configurations for conditional group visibility.
|
|
1658
|
+
* Only 'hidden' type logic is supported for groups.
|
|
1659
|
+
*/
|
|
1660
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1661
|
+
}
|
|
1662
|
+
/** Type guard for GroupField with proper type narrowing */
|
|
1663
|
+
declare function isGroupField(field: FieldDef<any>): field is GroupField;
|
|
1664
|
+
type GroupComponent<T extends GroupAllowedChildren[]> = FieldComponent<GroupField<T>>;
|
|
1665
|
+
|
|
1666
|
+
/** Container field interface for wrapping child fields with UI chrome. */
|
|
1667
|
+
interface ContainerField<TFields extends readonly ContainerAllowedChildren[] = readonly ContainerAllowedChildren[], TWrapperConfigs extends readonly WrapperConfig[] = readonly WrapperConfig[]> extends FieldDef<never> {
|
|
1668
|
+
type: 'container';
|
|
1669
|
+
/** Child definitions to render within this container */
|
|
1670
|
+
readonly fields: TFields;
|
|
1671
|
+
/**
|
|
1672
|
+
* Wrapper components to chain around the children.
|
|
1673
|
+
* Applied outermost-first: the first wrapper in the array is the outermost.
|
|
1674
|
+
* Each wrapper component receives the subsequent wrapper (or children) inside
|
|
1675
|
+
* its `#fieldComponent` ViewContainerRef slot.
|
|
1676
|
+
*/
|
|
1677
|
+
readonly wrappers: TWrapperConfigs;
|
|
1678
|
+
/** Container fields do not have a label property */
|
|
1679
|
+
readonly label?: never;
|
|
1680
|
+
/** Containers do not support meta — they have no native form element */
|
|
1681
|
+
readonly meta?: never;
|
|
1682
|
+
/**
|
|
1683
|
+
* Logic configurations for conditional container visibility.
|
|
1684
|
+
* Only 'hidden' type logic is supported for containers.
|
|
1685
|
+
*/
|
|
1686
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1687
|
+
}
|
|
1688
|
+
/** Type guard for ContainerField with proper type narrowing. */
|
|
1689
|
+
declare function isContainerTypedField(field: FieldDef<any>): field is ContainerField;
|
|
1690
|
+
type ContainerComponent = FieldComponent<ContainerField<ContainerAllowedChildren[]>>;
|
|
1691
|
+
|
|
1692
|
+
/**
|
|
1693
|
+
* Type constraints for field nesting rules
|
|
1694
|
+
* These ensure that container fields can only contain valid child field types
|
|
1695
|
+
*/
|
|
1696
|
+
/**
|
|
1697
|
+
* Fields that are allowed as children of Page fields
|
|
1698
|
+
* Pages can contain: rows, groups, arrays, and leaf fields (but NOT other pages)
|
|
1699
|
+
*/
|
|
1700
|
+
type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
1701
|
+
/**
|
|
1702
|
+
* Fields that are allowed as children of Group fields
|
|
1703
|
+
* Groups can contain: rows, arrays, and leaf fields (but NOT pages or other groups)
|
|
1704
|
+
*/
|
|
1705
|
+
type GroupAllowedChildren = LeafFieldTypes | RowField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
1706
|
+
/**
|
|
1707
|
+
* Fields that are allowed as children of Array fields
|
|
1708
|
+
* Arrays can contain: rows, groups, and leaf fields (but NOT pages or other arrays)
|
|
1709
|
+
* Groups are used for creating object arrays where each array item is an object
|
|
1710
|
+
*/
|
|
1711
|
+
type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField | ContainerField;
|
|
1712
|
+
/**
|
|
1713
|
+
* Fields that are allowed as children of Container fields.
|
|
1714
|
+
* Containers are pure layout primitives that flatten their children into the
|
|
1715
|
+
* parent form, so most registered field types may appear inside — including
|
|
1716
|
+
* hidden fields and other containers — but NOT pages.
|
|
1717
|
+
*/
|
|
1718
|
+
type ContainerAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
1719
|
+
/**
|
|
1720
|
+
* Row is a synthetic field type that resolves to a Container at runtime, so
|
|
1721
|
+
* it accepts the same children as a Container.
|
|
1722
|
+
*/
|
|
1723
|
+
type RowAllowedChildren = ContainerAllowedChildren;
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* An array item template is an array of allowed children that defines one OBJECT array item's fields.
|
|
1727
|
+
* Used for object arrays where each item is `{ key1: value1, key2: value2, ... }`.
|
|
1728
|
+
*/
|
|
1729
|
+
type ArrayItemTemplate = readonly ArrayAllowedChildren[];
|
|
1730
|
+
/**
|
|
1731
|
+
* An array item definition can be either:
|
|
1732
|
+
* - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
|
|
1733
|
+
* - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
|
|
1734
|
+
*/
|
|
1735
|
+
type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate;
|
|
1736
|
+
/** Array field interface for creating dynamic field collections that map to array values. */
|
|
1737
|
+
interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never> {
|
|
1738
|
+
type: 'array';
|
|
1739
|
+
/**
|
|
1740
|
+
* Array of item definitions. Each element defines one array item:
|
|
1741
|
+
* - Single FieldDef = primitive item (field's value is extracted directly)
|
|
1742
|
+
* - Array of FieldDefs = object item (fields are merged into an object)
|
|
1743
|
+
* - Empty array `[]` = no initial items (add via buttons with explicit templates)
|
|
1744
|
+
* - Items can have different configurations (heterogeneous arrays supported)
|
|
1745
|
+
* - Initial values are set via `value` property on each field definition
|
|
1746
|
+
*/
|
|
1747
|
+
readonly fields: TFields;
|
|
1748
|
+
/** Array fields do not have a label property * */
|
|
1749
|
+
readonly label?: never;
|
|
1750
|
+
/** Arrays do not support meta - they have no native form element * */
|
|
1751
|
+
readonly meta?: never;
|
|
1752
|
+
/**
|
|
1753
|
+
* Logic configurations for conditional array visibility.
|
|
1754
|
+
* Only 'hidden' type logic is supported for arrays.
|
|
1755
|
+
*/
|
|
1756
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1757
|
+
/**
|
|
1758
|
+
* Minimum number of items required in the array.
|
|
1759
|
+
* Validation fails if the array has fewer items than this value.
|
|
1760
|
+
*/
|
|
1761
|
+
readonly minLength?: number;
|
|
1762
|
+
/**
|
|
1763
|
+
* Maximum number of items allowed in the array.
|
|
1764
|
+
* Validation fails if the array has more items than this value.
|
|
1765
|
+
*/
|
|
1766
|
+
readonly maxLength?: number;
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Type guard for ArrayField with proper type narrowing.
|
|
1770
|
+
* Validates that the field is an array type with a fields property that is an array.
|
|
1771
|
+
* Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
|
|
1772
|
+
*/
|
|
1773
|
+
declare function isArrayField(field: FieldDef<any>): field is ArrayField;
|
|
1774
|
+
type ArrayComponent<T extends ArrayItemTemplate[]> = FieldComponent<ArrayField<T>>;
|
|
1775
|
+
/** Configuration for auto-generated add/remove buttons in simplified array fields. */
|
|
1776
|
+
interface ArrayButtonConfig {
|
|
1777
|
+
/** Custom label for the button */
|
|
1778
|
+
readonly label?: string;
|
|
1779
|
+
/** Additional properties passed to the button component */
|
|
1780
|
+
readonly props?: Record<string, unknown>;
|
|
1781
|
+
}
|
|
1782
|
+
/** Simplified array field interface for common use cases. */
|
|
1783
|
+
interface SimplifiedArrayField extends FieldDef<never> {
|
|
1784
|
+
type: 'array';
|
|
1785
|
+
/**
|
|
1786
|
+
* Template defining the structure of a single array item.
|
|
1787
|
+
* - Single field (ArrayAllowedChildren) → primitive array (each item is a single value)
|
|
1788
|
+
* - Array of fields (readonly ArrayAllowedChildren[]) → object array (each item is an object)
|
|
1789
|
+
*/
|
|
1790
|
+
readonly template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
|
|
1791
|
+
/** Initial values for the array. Each element creates one array item. */
|
|
1792
|
+
readonly value?: readonly unknown[];
|
|
1793
|
+
/**
|
|
1794
|
+
* Configuration for the auto-generated "Add" button, or `false` to disable it.
|
|
1795
|
+
* Defaults to a button with label "Add".
|
|
1796
|
+
*/
|
|
1797
|
+
readonly addButton?: ArrayButtonConfig | false;
|
|
1798
|
+
/**
|
|
1799
|
+
* Configuration for the auto-generated "Remove" button on each item, or `false` to disable it.
|
|
1800
|
+
* Defaults to a button with label "Remove".
|
|
1801
|
+
*/
|
|
1802
|
+
readonly removeButton?: ArrayButtonConfig | false;
|
|
1803
|
+
/** Simplified arrays do not support the label property */
|
|
1804
|
+
readonly label?: never;
|
|
1805
|
+
/** Simplified arrays do not support meta */
|
|
1806
|
+
readonly meta?: never;
|
|
1807
|
+
/**
|
|
1808
|
+
* Logic configurations for conditional array visibility.
|
|
1809
|
+
* Only 'hidden' type logic is supported for arrays.
|
|
1810
|
+
*/
|
|
1811
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1812
|
+
/**
|
|
1813
|
+
* Minimum number of items required in the array.
|
|
1814
|
+
* Validation fails if the array has fewer items than this value.
|
|
1815
|
+
*/
|
|
1816
|
+
readonly minLength?: number;
|
|
1817
|
+
/**
|
|
1818
|
+
* Maximum number of items allowed in the array.
|
|
1819
|
+
* Validation fails if the array has more items than this value.
|
|
1820
|
+
*/
|
|
1821
|
+
readonly maxLength?: number;
|
|
1822
|
+
/** Mutually exclusive with `template` — use `fields` for the full API instead */
|
|
1823
|
+
readonly fields?: never;
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1826
|
+
* Type guard for SimplifiedArrayField.
|
|
1827
|
+
* Checks for `type: 'array'` with a `template` property (discriminant from full ArrayField).
|
|
1828
|
+
*/
|
|
1829
|
+
declare function isSimplifiedArrayField(field: FieldDef<any>): field is SimplifiedArrayField;
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Scalar types supported by hidden fields.
|
|
1833
|
+
* These are the primitive value types that can be stored.
|
|
1834
|
+
*/
|
|
1835
|
+
type HiddenScalar = string | number | boolean;
|
|
1836
|
+
/**
|
|
1837
|
+
* All value types supported by hidden fields.
|
|
1838
|
+
* Supports both scalar values and arrays of scalars for storing
|
|
1839
|
+
* multiple IDs or similar data.
|
|
1840
|
+
*/
|
|
1841
|
+
type HiddenValue = HiddenScalar | HiddenScalar[];
|
|
1842
|
+
/**
|
|
1843
|
+
* Hidden field definition for storing values without rendering UI.
|
|
1844
|
+
*
|
|
1845
|
+
* @typeParam TValue - The type of value stored (must be HiddenValue compatible)
|
|
1846
|
+
*/
|
|
1847
|
+
interface HiddenField<TValue extends HiddenValue = HiddenValue> extends FieldDef<never> {
|
|
1848
|
+
/** Discriminant for hidden field type */
|
|
1849
|
+
type: 'hidden';
|
|
1850
|
+
/**
|
|
1851
|
+
* The value to store in the form.
|
|
1852
|
+
* This is required - a hidden field without a value serves no purpose.
|
|
1853
|
+
*/
|
|
1854
|
+
value: TValue;
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Type guard to check if a field is a hidden field.
|
|
1858
|
+
*
|
|
1859
|
+
* @param field - The field to check
|
|
1860
|
+
* @returns True if the field is a HiddenField
|
|
1861
|
+
*/
|
|
1862
|
+
declare function isHiddenField(field: FieldDef<unknown>): field is HiddenField;
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* Page field interface for creating top-level page layouts
|
|
1866
|
+
* This is a special field type that contains other field definitions
|
|
1867
|
+
* The page itself doesn't have a value - it's a layout container like row
|
|
1868
|
+
* Pages can only be used at the top level and cannot be nested
|
|
1869
|
+
* This is a programmatic field type only - users cannot customize this field type
|
|
1870
|
+
*/
|
|
1871
|
+
interface PageField<TFields extends readonly PageAllowedChildren[] = PageAllowedChildren[]> extends FieldDef<never> {
|
|
1872
|
+
type: 'page';
|
|
1873
|
+
/** Child field definitions to render within this page */
|
|
1874
|
+
readonly fields: TFields;
|
|
1875
|
+
/** Page fields do not have a label property * */
|
|
1876
|
+
readonly label?: never;
|
|
1877
|
+
/** Pages do not support meta - they have no native form element * */
|
|
1878
|
+
readonly meta?: never;
|
|
1879
|
+
/**
|
|
1880
|
+
* Logic configurations for conditional page visibility.
|
|
1881
|
+
* Only 'hidden' type logic is supported for pages.
|
|
1882
|
+
*/
|
|
1883
|
+
readonly logic?: ContainerLogicConfig[];
|
|
1884
|
+
}
|
|
1885
|
+
/** Type guard for PageField with proper type narrowing */
|
|
1886
|
+
declare function isPageField(field: FieldDef<unknown>): field is PageField;
|
|
1887
|
+
/**
|
|
1888
|
+
* Validates that a page field doesn't contain nested page fields
|
|
1889
|
+
*
|
|
1890
|
+
* @param pageField The page field to validate
|
|
1891
|
+
* @returns true if valid (no nested pages), false otherwise
|
|
1892
|
+
*/
|
|
1893
|
+
declare function validatePageNesting(pageField: PageField): boolean;
|
|
1894
|
+
|
|
1895
|
+
/** Text element type for rendering different HTML text elements */
|
|
1896
|
+
type TextElementType = 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span';
|
|
1897
|
+
/** Properties for text field configuration */
|
|
1898
|
+
type TextProps = {
|
|
1899
|
+
/** The HTML element type to render */
|
|
1900
|
+
elementType: TextElementType;
|
|
1901
|
+
};
|
|
1902
|
+
/** Text field definition for displaying translatable text content */
|
|
1903
|
+
interface TextField extends FieldDef<TextProps> {
|
|
1904
|
+
type: 'text';
|
|
1905
|
+
/**
|
|
1906
|
+
* Logic rules for conditional visibility.
|
|
1907
|
+
* Text fields only support 'hidden' logic type since they are display-only
|
|
1908
|
+
* and don't participate in form validation (no disabled/readonly/required).
|
|
1909
|
+
*/
|
|
1910
|
+
readonly logic?: NonFieldLogicConfig[];
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/** Configuration for the built-in 'css' wrapper type. */
|
|
1914
|
+
interface CssWrapper {
|
|
1915
|
+
readonly type: 'css';
|
|
1916
|
+
/** CSS classes to apply to the wrapper */
|
|
1917
|
+
readonly cssClasses?: DynamicText;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
/** Configuration for the built-in 'row' wrapper type. */
|
|
1921
|
+
interface RowWrapper {
|
|
1922
|
+
readonly type: 'row';
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
/** Container fields registry - augment this interface to add custom container fields */
|
|
1926
|
+
interface FieldRegistryContainers {
|
|
1927
|
+
page: PageField;
|
|
1928
|
+
row: RowField;
|
|
1929
|
+
group: GroupField;
|
|
1930
|
+
array: ArrayField | SimplifiedArrayField;
|
|
1931
|
+
container: ContainerField;
|
|
1932
|
+
}
|
|
1933
|
+
/** Leaf fields registry - augment this interface to add custom leaf fields */
|
|
1934
|
+
interface FieldRegistryLeaves {
|
|
1935
|
+
text: TextField;
|
|
1936
|
+
hidden: HiddenField;
|
|
1937
|
+
}
|
|
1938
|
+
/** Wrapper type registry for module augmentation. */
|
|
1939
|
+
interface FieldRegistryWrappers {
|
|
1940
|
+
css: CssWrapper;
|
|
1941
|
+
row: RowWrapper;
|
|
1942
|
+
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Global interface for dynamic form field definitions with categorization
|
|
1945
|
+
* This interface combines containers and leaves from their respective registries
|
|
1946
|
+
*/
|
|
1947
|
+
interface DynamicFormFieldRegistry {
|
|
1948
|
+
/** Container fields that hold other fields (no value, have children) */
|
|
1949
|
+
containers: FieldRegistryContainers;
|
|
1950
|
+
/** Leaf fields that have values or display content */
|
|
1951
|
+
leaves: FieldRegistryLeaves;
|
|
1952
|
+
/** Field wrappers types */
|
|
1953
|
+
wrappers: FieldRegistryWrappers;
|
|
1954
|
+
}
|
|
1955
|
+
/** Union type of all registered container field definitions */
|
|
1956
|
+
type ContainerFieldTypes = DynamicFormFieldRegistry['containers'][keyof DynamicFormFieldRegistry['containers']];
|
|
1957
|
+
/** Union type of all registered leaf field definitions */
|
|
1958
|
+
type LeafFieldTypes = DynamicFormFieldRegistry['leaves'][keyof DynamicFormFieldRegistry['leaves']];
|
|
1959
|
+
/** Union type of all registered field definitions */
|
|
1960
|
+
type RegisteredFieldTypes = ContainerFieldTypes | LeafFieldTypes;
|
|
1961
|
+
/** Extract field types that are available in the registry */
|
|
1962
|
+
type AvailableFieldTypes = keyof DynamicFormFieldRegistry['containers'] | keyof DynamicFormFieldRegistry['leaves'];
|
|
1963
|
+
/** Extract wrapper types that are available in the registry */
|
|
1964
|
+
type RegisteredWrapperTypes = keyof DynamicFormFieldRegistry['wrappers'];
|
|
1965
|
+
/**
|
|
1966
|
+
* Combined registry mapping type names to field definitions.
|
|
1967
|
+
* This flattens containers and leaves into a single mapping.
|
|
1968
|
+
*/
|
|
1969
|
+
type FieldTypeMap = DynamicFormFieldRegistry['containers'] & DynamicFormFieldRegistry['leaves'];
|
|
1970
|
+
/**
|
|
1971
|
+
* Extract a specific field type from RegisteredFieldTypes based on the `type` discriminant.
|
|
1972
|
+
* This enables proper type narrowing when defining fields.
|
|
1973
|
+
*/
|
|
1974
|
+
type ExtractField<T extends AvailableFieldTypes> = T extends keyof FieldTypeMap ? FieldTypeMap[T] : never;
|
|
1975
|
+
/**
|
|
1976
|
+
* Narrow a field definition based on its `type` property.
|
|
1977
|
+
* Use this to get proper type inference when working with field unions.
|
|
1978
|
+
*/
|
|
1979
|
+
type NarrowField<T> = T extends {
|
|
1980
|
+
type: infer TType;
|
|
1981
|
+
} ? (TType extends AvailableFieldTypes ? ExtractField<TType> : T) : T;
|
|
1982
|
+
/**
|
|
1983
|
+
* Narrow each field in an array based on its `type` property.
|
|
1984
|
+
* Use with `satisfies` to get proper type inference for field arrays.
|
|
1985
|
+
*/
|
|
1986
|
+
type NarrowFields = readonly NarrowField<RegisteredFieldTypes>[];
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
* Helper type to convert union to intersection.
|
|
1990
|
+
* Uses `U extends U` to trigger distributive conditional type behavior.
|
|
1991
|
+
*/
|
|
1992
|
+
type UnionToIntersection<U> = (U extends U ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
1993
|
+
/** Depth counter for recursion limiting (prevents infinite type instantiation) */
|
|
1994
|
+
type Depth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
1995
|
+
/**
|
|
1996
|
+
* Widens literal types to their primitive equivalents, recursively for objects and arrays.
|
|
1997
|
+
* This prevents `as const` from over-narrowing types like `''` to literal `''` instead of `string`.
|
|
1998
|
+
*/
|
|
1999
|
+
type Widen<T, D extends number = 5> = [D] extends [never] ? T : T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends readonly (infer U)[] ? Widen<U, Depth[D]>[] : T extends Record<string, unknown> ? {
|
|
2000
|
+
-readonly [K in keyof T]: Widen<T[K], Depth[D]>;
|
|
2001
|
+
} : T;
|
|
2002
|
+
/**
|
|
2003
|
+
* Resolves the "underlying" non-null value type for a field, based on its `type`
|
|
2004
|
+
* discriminator and (where it matters) its `value` literal. Used when we need a
|
|
2005
|
+
* base type independent of whether the literal happens to be `null` — i.e., when
|
|
2006
|
+
* computing the nullable widening for `value: null, nullable: true`.
|
|
2007
|
+
*/
|
|
2008
|
+
type ResolveBaseValueType<T, V> = T extends {
|
|
2009
|
+
type: 'slider';
|
|
2010
|
+
} ? number : T extends {
|
|
2011
|
+
type: 'input';
|
|
2012
|
+
props: {
|
|
2013
|
+
type: 'number';
|
|
2014
|
+
};
|
|
2015
|
+
} ? number : T extends {
|
|
2016
|
+
type: 'checkbox' | 'toggle';
|
|
2017
|
+
} ? boolean : [V] extends [null] ? string : Widen<V>;
|
|
2018
|
+
/**
|
|
2019
|
+
* Infer value type based on field type, value literal, and the `nullable` flag.
|
|
2020
|
+
* - Slider fields: always number
|
|
2021
|
+
* - Input fields with props.type: 'number': number
|
|
2022
|
+
* - Checkbox / toggle fields: always boolean
|
|
2023
|
+
* - Other fields: widen the literal value type (default to string when value is null)
|
|
2024
|
+
* - When `nullable: true` is declared, widens the result to `T | null`.
|
|
2025
|
+
*/
|
|
2026
|
+
type InferValueType<T, V> = T extends {
|
|
2027
|
+
nullable: true;
|
|
2028
|
+
} ? ResolveBaseValueType<T, V> | null : ResolveBaseValueType<T, V>;
|
|
2029
|
+
/**
|
|
2030
|
+
* Make type optional if field is not required.
|
|
2031
|
+
* Hidden fields are never optional since they require a value property.
|
|
2032
|
+
*/
|
|
2033
|
+
type MaybeOptional<T, V> = T extends {
|
|
2034
|
+
type: 'hidden';
|
|
2035
|
+
} ? V : T extends {
|
|
2036
|
+
required: true;
|
|
2037
|
+
} ? V : V | undefined;
|
|
2038
|
+
/**
|
|
2039
|
+
* Infers the value type from a single template field (for primitive arrays).
|
|
2040
|
+
* Mirrors InferValueType logic: slider → number, input[number] → number,
|
|
2041
|
+
* checkbox/toggle → boolean, else widen `value` (string fallback). Widens to
|
|
2042
|
+
* `T | null` when `nullable: true` is declared on the template field.
|
|
2043
|
+
*/
|
|
2044
|
+
type InferSingleTemplateValueBase<T, D extends number> = T extends {
|
|
2045
|
+
type: 'slider';
|
|
2046
|
+
} ? number : T extends {
|
|
2047
|
+
type: 'input';
|
|
2048
|
+
props: {
|
|
2049
|
+
type: 'number';
|
|
2050
|
+
};
|
|
2051
|
+
} ? number : T extends {
|
|
2052
|
+
type: 'checkbox' | 'toggle';
|
|
2053
|
+
} ? boolean : T extends {
|
|
2054
|
+
value: infer V;
|
|
2055
|
+
} ? [V] extends [null] ? string : Widen<V, Depth[D]> : string;
|
|
2056
|
+
type InferSingleTemplateValue<T, D extends number> = T extends {
|
|
2057
|
+
nullable: true;
|
|
2058
|
+
} ? InferSingleTemplateValueBase<T, D> | null : InferSingleTemplateValueBase<T, D>;
|
|
2059
|
+
/**
|
|
2060
|
+
* Infers the value type for an array field.
|
|
2061
|
+
* Handles four cases:
|
|
2062
|
+
* 1. Simplified array with template + value → infer item type from value
|
|
2063
|
+
* 2. Simplified array with array template (no value) → infer object type from template fields
|
|
2064
|
+
* 3. Simplified array with single-field template (no value) → infer primitive type from template field
|
|
2065
|
+
* 4. Full-API array with fields → infer from children
|
|
2066
|
+
*/
|
|
2067
|
+
type InferArrayValue<T, D extends number> = T extends {
|
|
2068
|
+
template: unknown;
|
|
2069
|
+
value: readonly (infer Item)[];
|
|
2070
|
+
} ? Widen<Item>[] : T extends {
|
|
2071
|
+
template: infer TArr;
|
|
2072
|
+
} ? TArr extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<[...TArr], Depth[D]>[] : InferSingleTemplateValue<TArr, D>[] : T extends {
|
|
2073
|
+
fields: infer F;
|
|
2074
|
+
} ? F extends RegisteredFieldTypes[] ? InferFormValueWithDepth<F, Depth[D]>[] : unknown[] : unknown[];
|
|
2075
|
+
/** Process a single field and determine its contribution to the form value type */
|
|
2076
|
+
type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string, unknown> : T extends {
|
|
2077
|
+
type: 'page' | 'row';
|
|
2078
|
+
fields: infer F;
|
|
2079
|
+
} ? F extends RegisteredFieldTypes[] ? InferFormValueWithDepth<F, Depth[D]> : never : T extends {
|
|
2080
|
+
type: 'group';
|
|
2081
|
+
key: infer K;
|
|
2082
|
+
fields: infer F;
|
|
2083
|
+
} ? K extends string ? F extends RegisteredFieldTypes[] ? {
|
|
2084
|
+
[P in K]: InferFormValueWithDepth<F, Depth[D]>;
|
|
2085
|
+
} : {
|
|
2086
|
+
[P in K]: Record<string, unknown>;
|
|
2087
|
+
} : never : T extends {
|
|
2088
|
+
type: 'array';
|
|
2089
|
+
key: infer K;
|
|
2090
|
+
} ? K extends string ? {
|
|
2091
|
+
[P in K]: InferArrayValue<T, D>;
|
|
2092
|
+
} : never : T extends {
|
|
2093
|
+
type: 'text';
|
|
2094
|
+
} ? never : T extends {
|
|
2095
|
+
type: 'submit' | 'button' | 'next' | 'previous' | 'addArrayItem' | 'removeArrayItem';
|
|
2096
|
+
} ? never : T extends {
|
|
2097
|
+
key: infer K;
|
|
2098
|
+
value: infer V;
|
|
2099
|
+
} ? K extends string ? {
|
|
2100
|
+
[P in K]: MaybeOptional<T, InferValueType<T, V>>;
|
|
2101
|
+
} : never : T extends {
|
|
2102
|
+
key: infer K;
|
|
2103
|
+
} ? K extends string ? {
|
|
2104
|
+
[P in K]: MaybeOptional<T, T extends {
|
|
2105
|
+
nullable: true;
|
|
2106
|
+
} ? string | null : string>;
|
|
2107
|
+
} : never : never;
|
|
2108
|
+
/** Internal helper with depth tracking */
|
|
2109
|
+
type InferFormValueWithDepth<T extends RegisteredFieldTypes[], D extends number = 5> = UnionToIntersection<ProcessField<T[number], D>>;
|
|
2110
|
+
/** Helper to extract fields from either fields array or FormConfig */
|
|
2111
|
+
type ExtractFields<T> = T extends {
|
|
2112
|
+
fields: infer TFields;
|
|
2113
|
+
} ? TFields extends readonly RegisteredFieldTypes[] ? TFields : never : T extends readonly RegisteredFieldTypes[] ? T : never;
|
|
2114
|
+
/**
|
|
2115
|
+
* Infer form value type from fields array or FormConfig.
|
|
2116
|
+
* Recursively processes nested structures and merges the results.
|
|
2117
|
+
* Limited to 5 levels of nesting to prevent infinite type instantiation.
|
|
2118
|
+
*/
|
|
2119
|
+
type InferFormValue<T> = ExtractFields<T> extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<ExtractFields<T> extends RegisteredFieldTypes[] ? ExtractFields<T> : [...ExtractFields<T>], 5> : never;
|
|
2120
|
+
|
|
2121
|
+
/**
|
|
2122
|
+
* Re-export Angular's ValidationError for consistency
|
|
2123
|
+
* This replaces our custom ValidationError interface
|
|
2124
|
+
*/
|
|
2125
|
+
type ValidationError = ValidationError$1;
|
|
2126
|
+
interface ValidationMessages {
|
|
2127
|
+
required?: DynamicText;
|
|
2128
|
+
email?: DynamicText;
|
|
2129
|
+
min?: DynamicText;
|
|
2130
|
+
max?: DynamicText;
|
|
2131
|
+
minLength?: DynamicText;
|
|
2132
|
+
maxLength?: DynamicText;
|
|
2133
|
+
pattern?: DynamicText;
|
|
2134
|
+
[key: string]: DynamicText | undefined;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
/**
|
|
2138
|
+
* The result type for submission actions.
|
|
2139
|
+
* Can be either a Promise or an Observable.
|
|
2140
|
+
*/
|
|
2141
|
+
type SubmissionActionResult = Promise<TreeValidationResult> | Observable<TreeValidationResult | unknown>;
|
|
2142
|
+
/**
|
|
2143
|
+
* Configuration for form submission handling.
|
|
2144
|
+
*
|
|
2145
|
+
* @typeParam TValue - The form value type
|
|
2146
|
+
*/
|
|
2147
|
+
interface SubmissionConfig<TValue = unknown> {
|
|
2148
|
+
/**
|
|
2149
|
+
* Action called when the form is submitted.
|
|
2150
|
+
*
|
|
2151
|
+
* @param form - The form's FieldTree instance
|
|
2152
|
+
* @returns Promise or Observable - for errors, return TreeValidationResult
|
|
2153
|
+
*/
|
|
2154
|
+
action: (form: FieldTree<TValue>) => SubmissionActionResult;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
/**
|
|
2158
|
+
* Configuration interface for defining dynamic form structure and behavior.
|
|
2159
|
+
*
|
|
2160
|
+
* @typeParam TFields - Array of registered field types available for this form
|
|
2161
|
+
* @typeParam TValue - The strongly-typed interface for form values
|
|
2162
|
+
* @typeParam TProps - The type for form-level default props (library-specific)
|
|
2163
|
+
*/
|
|
2164
|
+
interface FormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = RegisteredFieldTypes[], TValue = InferFormValue<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>, TProps extends object = Record<string, unknown>, TSchemaValue = unknown> {
|
|
2165
|
+
/** Array of field definitions that define the form structure. */
|
|
2166
|
+
fields: TFields;
|
|
2167
|
+
/** Optional form-level validation schema using Standard Schema spec. */
|
|
2168
|
+
schema?: FormSchema<TSchemaValue>;
|
|
2169
|
+
/**
|
|
2170
|
+
* Global form configuration options.
|
|
2171
|
+
*
|
|
2172
|
+
* @value {}
|
|
2173
|
+
*/
|
|
2174
|
+
options?: FormOptions;
|
|
2175
|
+
/** Global schemas available to all fields. */
|
|
2176
|
+
schemas?: SchemaDefinition[];
|
|
2177
|
+
/** Form-level validation messages that act as fallback for field-level messages. */
|
|
2178
|
+
defaultValidationMessages?: ValidationMessages;
|
|
2179
|
+
/** Signal forms adapter configuration. */
|
|
2180
|
+
customFnConfig?: CustomFnConfig<TValue extends Record<string, unknown> ? TValue : Record<string, unknown>>;
|
|
2181
|
+
/** Form submission configuration. */
|
|
2182
|
+
submission?: SubmissionConfig<TValue>;
|
|
2183
|
+
/** Default props applied to all fields in the form. */
|
|
2184
|
+
defaultProps?: TProps;
|
|
2185
|
+
/** External data signals available to conditional logic and derivations. */
|
|
2186
|
+
externalData?: Record<string, Signal<unknown>>;
|
|
2187
|
+
/** Form-wide default wrappers applied to every field that does not explicitly opt out. */
|
|
2188
|
+
defaultWrappers?: readonly WrapperConfig[];
|
|
2189
|
+
}
|
|
2190
|
+
/** Signal forms adapter configuration for advanced form behavior. */
|
|
2191
|
+
interface CustomFnConfig<TFormValue extends Record<string, unknown> = Record<string, unknown>> {
|
|
2192
|
+
/** Custom evaluation functions for conditional expressions. */
|
|
2193
|
+
customFunctions?: Record<string, CustomFunction<TFormValue>>;
|
|
2194
|
+
/** Custom derivation functions for value derivation logic. */
|
|
2195
|
+
derivations?: Record<string, CustomFunction<TFormValue>>;
|
|
2196
|
+
/** Async derivation functions for asynchronous value derivation logic. */
|
|
2197
|
+
asyncDerivations?: Record<string, AsyncDerivationFunction<TFormValue>>;
|
|
2198
|
+
/** Async condition functions for asynchronous field state logic. */
|
|
2199
|
+
asyncConditions?: Record<string, AsyncConditionFunction<TFormValue>>;
|
|
2200
|
+
/** Custom validators using Angular's public FieldContext API */
|
|
2201
|
+
validators?: Record<string, CustomValidator>;
|
|
2202
|
+
/** Async custom validators using Angular's resource-based validateAsync() API */
|
|
2203
|
+
asyncValidators?: Record<string, AsyncCustomValidator>;
|
|
2204
|
+
/** HTTP validators using Angular's validateHttp() API. */
|
|
2205
|
+
httpValidators?: Record<string, HttpCustomValidator>;
|
|
2206
|
+
}
|
|
2207
|
+
/** Global form configuration options. */
|
|
2208
|
+
interface FormOptions {
|
|
2209
|
+
/**
|
|
2210
|
+
* Disable the entire form.
|
|
2211
|
+
*
|
|
2212
|
+
* @value false
|
|
2213
|
+
*/
|
|
2214
|
+
disabled?: boolean;
|
|
2215
|
+
/**
|
|
2216
|
+
* Prefix scoping every field's DOM `id` (and matching `for` / `data-testid` /
|
|
2217
|
+
* `aria-describedby`) to this form instance, as the outermost id segment:
|
|
2218
|
+
* `{idPrefix}_{group}_{key}_{index}`. Use it when rendering multiple forms
|
|
2219
|
+
* from the same config on one page so their ids don't collide.
|
|
2220
|
+
*
|
|
2221
|
+
* When omitted, a lone form stays unprefixed and a generated prefix (`df-1`,
|
|
2222
|
+
* `df-2`, …) is applied automatically once a second form is mounted. Characters
|
|
2223
|
+
* invalid in an id token (whitespace, punctuation) are replaced with `_`.
|
|
2224
|
+
*
|
|
2225
|
+
* @example
|
|
2226
|
+
* ```typescript
|
|
2227
|
+
* options: { idPrefix: 'billing' } // → id="billing_email-input"
|
|
2228
|
+
* ```
|
|
2229
|
+
*
|
|
2230
|
+
* @default undefined (auto-prefix only when multiple forms are mounted)
|
|
2231
|
+
*/
|
|
2232
|
+
idPrefix?: string;
|
|
2233
|
+
/**
|
|
2234
|
+
* Maximum number of iterations for derivation chain processing.
|
|
2235
|
+
*
|
|
2236
|
+
* @default 10
|
|
2237
|
+
*/
|
|
2238
|
+
maxDerivationIterations?: number;
|
|
2239
|
+
/** Default disabled behavior for submit buttons. */
|
|
2240
|
+
submitButton?: SubmitButtonOptions;
|
|
2241
|
+
/** Default disabled behavior for next page buttons. */
|
|
2242
|
+
nextButton?: NextButtonOptions;
|
|
2243
|
+
/**
|
|
2244
|
+
* Whether to exclude values of hidden fields from submission output.
|
|
2245
|
+
*
|
|
2246
|
+
* @default undefined (uses global setting)
|
|
2247
|
+
*/
|
|
2248
|
+
excludeValueIfHidden?: boolean;
|
|
2249
|
+
/**
|
|
2250
|
+
* Whether to exclude values of disabled fields from submission output.
|
|
2251
|
+
*
|
|
2252
|
+
* @default undefined (uses global setting)
|
|
2253
|
+
*/
|
|
2254
|
+
excludeValueIfDisabled?: boolean;
|
|
2255
|
+
/**
|
|
2256
|
+
* Whether to exclude values of readonly fields from submission output.
|
|
2257
|
+
*
|
|
2258
|
+
* @default undefined (uses global setting)
|
|
2259
|
+
*/
|
|
2260
|
+
excludeValueIfReadonly?: boolean;
|
|
2261
|
+
/**
|
|
2262
|
+
* Whether to run validation when a field is hidden.
|
|
2263
|
+
*
|
|
2264
|
+
* @default undefined (uses global setting, which defaults to `false`)
|
|
2265
|
+
*/
|
|
2266
|
+
validateWhenHidden?: boolean;
|
|
2267
|
+
/**
|
|
2268
|
+
* Whether to attach the current form value to all events dispatched through the EventBus.
|
|
2269
|
+
*
|
|
2270
|
+
* @default undefined (uses global setting)
|
|
2271
|
+
*/
|
|
2272
|
+
emitFormValueOnEvents?: boolean;
|
|
2273
|
+
}
|
|
2274
|
+
/** Options for controlling submit button disabled behavior. */
|
|
2275
|
+
interface SubmitButtonOptions {
|
|
2276
|
+
/**
|
|
2277
|
+
* Disable submit button when the form is invalid.
|
|
2278
|
+
*
|
|
2279
|
+
* @default true
|
|
2280
|
+
*/
|
|
2281
|
+
disableWhenInvalid?: boolean;
|
|
2282
|
+
/**
|
|
2283
|
+
* Disable submit button while the form is submitting.
|
|
2284
|
+
*
|
|
2285
|
+
* @default true
|
|
2286
|
+
*/
|
|
2287
|
+
disableWhileSubmitting?: boolean;
|
|
2288
|
+
}
|
|
2289
|
+
/** Options for controlling next page button disabled behavior. */
|
|
2290
|
+
interface NextButtonOptions {
|
|
2291
|
+
/**
|
|
2292
|
+
* Disable next button when the current page has invalid fields.
|
|
2293
|
+
*
|
|
2294
|
+
* @default true
|
|
2295
|
+
*/
|
|
2296
|
+
disableWhenPageInvalid?: boolean;
|
|
2297
|
+
/**
|
|
2298
|
+
* Disable next button while the form is submitting.
|
|
2299
|
+
*
|
|
2300
|
+
* @default true
|
|
2301
|
+
*/
|
|
2302
|
+
disableWhileSubmitting?: boolean;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
/** Context for resolving button disabled state. */
|
|
2306
|
+
interface ButtonLogicContext {
|
|
2307
|
+
/** The form's FieldTree instance (supports both string and number keys for array indices) */
|
|
2308
|
+
form: FieldTree<unknown, string | number>;
|
|
2309
|
+
/** Form-level options */
|
|
2310
|
+
formOptions?: FormOptions;
|
|
2311
|
+
/** Field-level logic array (if provided) */
|
|
2312
|
+
fieldLogic?: LogicConfig[];
|
|
2313
|
+
/** Explicit disabled state from field definition */
|
|
2314
|
+
explicitlyDisabled?: boolean;
|
|
2315
|
+
/** Current page validity signal (for paged forms) */
|
|
2316
|
+
currentPageValid?: Signal<boolean>;
|
|
2317
|
+
/** Current form value for evaluating conditional expressions */
|
|
2318
|
+
formValue?: unknown;
|
|
2319
|
+
/** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
|
|
2320
|
+
logger?: Logger;
|
|
2321
|
+
}
|
|
2322
|
+
/**
|
|
2323
|
+
* Allowed logic types for non-form-bound elements (buttons, text fields, etc.).
|
|
2324
|
+
* These elements only support hidden and disabled states - not readonly or required
|
|
2325
|
+
* since they don't participate in form validation.
|
|
2326
|
+
*/
|
|
2327
|
+
type NonFieldLogicType = 'hidden' | 'disabled';
|
|
2328
|
+
/**
|
|
2329
|
+
* Logic config restricted to types valid for non-form-bound elements.
|
|
2330
|
+
* This is a subset of LogicConfig that only includes hidden and disabled types.
|
|
2331
|
+
*/
|
|
2332
|
+
type NonFieldLogicConfig = LogicConfig & {
|
|
2333
|
+
type: NonFieldLogicType;
|
|
2334
|
+
};
|
|
2335
|
+
/** Context for resolving state (hidden/disabled) for non-form-bound elements. */
|
|
2336
|
+
interface NonFieldLogicContext {
|
|
2337
|
+
/** The form's FieldTree instance (supports both string and number keys for array indices) */
|
|
2338
|
+
form: FieldTree<unknown, string | number>;
|
|
2339
|
+
/**
|
|
2340
|
+
* Field-level logic array containing conditions.
|
|
2341
|
+
* Accepts full LogicConfig[] for compatibility, but only hidden and disabled types are processed.
|
|
2342
|
+
*/
|
|
2343
|
+
fieldLogic?: LogicConfig[];
|
|
2344
|
+
/** Explicit state from field definition (e.g., `hidden: true` or `disabled: true`) */
|
|
2345
|
+
explicitValue?: boolean;
|
|
2346
|
+
/** Current form value for evaluating conditional expressions */
|
|
2347
|
+
formValue?: unknown;
|
|
2348
|
+
/** Optional logger for diagnostic output. Falls back to no-op logger if not provided. */
|
|
2349
|
+
logger?: Logger;
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* Resolves the disabled state for a submit button.
|
|
2353
|
+
*
|
|
2354
|
+
* @param ctx - The button logic context
|
|
2355
|
+
* @returns A computed signal that returns true when the button should be disabled
|
|
2356
|
+
*/
|
|
2357
|
+
declare function resolveSubmitButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
|
|
2358
|
+
/**
|
|
2359
|
+
* Resolves the disabled state for a next page button.
|
|
2360
|
+
*
|
|
2361
|
+
* @param ctx - The button logic context
|
|
2362
|
+
* @returns A computed signal that returns true when the button should be disabled
|
|
2363
|
+
*/
|
|
2364
|
+
declare function resolveNextButtonDisabled(ctx: ButtonLogicContext): Signal<boolean>;
|
|
2365
|
+
/**
|
|
2366
|
+
* Evaluates the hidden state for non-form-bound elements synchronously.
|
|
2367
|
+
*
|
|
2368
|
+
* @param ctx - The context containing form, logic array, and explicit hidden state
|
|
2369
|
+
* @returns true when the element should be hidden
|
|
2370
|
+
*/
|
|
2371
|
+
declare function evaluateNonFieldHidden(ctx: NonFieldLogicContext): boolean;
|
|
2372
|
+
/**
|
|
2373
|
+
* Resolves the hidden state for non-form-bound elements as a reactive signal.
|
|
2374
|
+
*
|
|
2375
|
+
* @param ctx - The context containing form, logic array, and explicit hidden state
|
|
2376
|
+
* @returns A computed signal that returns true when the element should be hidden
|
|
2377
|
+
*/
|
|
2378
|
+
declare function resolveNonFieldHidden(ctx: NonFieldLogicContext): Signal<boolean>;
|
|
2379
|
+
/**
|
|
2380
|
+
* Evaluates the disabled state for non-form-bound elements synchronously.
|
|
2381
|
+
*
|
|
2382
|
+
* @param ctx - The context containing form, logic array, and explicit disabled state
|
|
2383
|
+
* @returns true when the element should be disabled
|
|
2384
|
+
*/
|
|
2385
|
+
declare function evaluateNonFieldDisabled(ctx: NonFieldLogicContext): boolean;
|
|
2386
|
+
/**
|
|
2387
|
+
* Resolves the disabled state for non-form-bound elements as a reactive signal.
|
|
2388
|
+
*
|
|
2389
|
+
* @param ctx - The context containing form, logic array, and explicit disabled state
|
|
2390
|
+
* @returns A computed signal that returns true when the element should be disabled
|
|
2391
|
+
*/
|
|
2392
|
+
declare function resolveNonFieldDisabled(ctx: NonFieldLogicContext): Signal<boolean>;
|
|
2393
|
+
|
|
2394
|
+
/**
|
|
2395
|
+
* Service that provides field evaluation context by combining
|
|
2396
|
+
* field context with root form registry information.
|
|
2397
|
+
*/
|
|
2398
|
+
declare class FieldContextRegistryService {
|
|
2399
|
+
private rootFormRegistry;
|
|
2400
|
+
private logger;
|
|
2401
|
+
private deprecationTracker;
|
|
2402
|
+
private externalDataSignal;
|
|
2403
|
+
/**
|
|
2404
|
+
* Creates an evaluation context for a field by combining:
|
|
2405
|
+
* - The field's current value
|
|
2406
|
+
* - The root form value from the registry
|
|
2407
|
+
* - The field path (if available from form context)
|
|
2408
|
+
* - Custom functions (if provided)
|
|
2409
|
+
*/
|
|
2410
|
+
createEvaluationContext<TValue>(fieldContext: FieldContext<TValue>, customFunctions?: Record<string, (context: EvaluationContext) => unknown>): EvaluationContext;
|
|
2411
|
+
/** Extracts the field path (key) for a given field context. */
|
|
2412
|
+
private extractFieldPath;
|
|
2413
|
+
/** Builds an evaluation context scoped to a specific array item. */
|
|
2414
|
+
private buildArrayScopedContext;
|
|
2415
|
+
/**
|
|
2416
|
+
* Resolves external data signals to their current values.
|
|
2417
|
+
*
|
|
2418
|
+
* @param reactive - If true, reads signals reactively (creates dependencies).
|
|
2419
|
+
* If false, reads signals with untracked() (no dependencies).
|
|
2420
|
+
* @returns Record of resolved external data values, or undefined if no external data.
|
|
2421
|
+
*/
|
|
2422
|
+
private resolveExternalData;
|
|
2423
|
+
/** Creates a REACTIVE evaluation context for logic functions. */
|
|
2424
|
+
createReactiveEvaluationContext<TValue>(fieldContext: FieldContext<TValue>, customFunctions?: Record<string, (context: EvaluationContext) => unknown>): EvaluationContext;
|
|
2425
|
+
/**
|
|
2426
|
+
* Creates an evaluation context for display-only components (text fields, pages)
|
|
2427
|
+
* that don't have their own FieldContext.
|
|
2428
|
+
*
|
|
2429
|
+
* @param fieldPath - The key/path of the display-only component
|
|
2430
|
+
* @param customFunctions - Optional custom functions for expression evaluation
|
|
2431
|
+
*/
|
|
2432
|
+
createDisplayOnlyContext(fieldPath: string, customFunctions?: Record<string, (context: EvaluationContext) => unknown>): EvaluationContext;
|
|
2433
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FieldContextRegistryService, never>;
|
|
2434
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<FieldContextRegistryService>;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
/** Registry service for custom functions and validators */
|
|
2438
|
+
declare class FunctionRegistryService {
|
|
2439
|
+
private readonly customFunctions;
|
|
2440
|
+
private readonly customFunctionScopes;
|
|
2441
|
+
private readonly derivationFunctions;
|
|
2442
|
+
private readonly asyncDerivationFunctions;
|
|
2443
|
+
private readonly asyncConditionFunctions;
|
|
2444
|
+
private readonly validators;
|
|
2445
|
+
private readonly asyncValidators;
|
|
2446
|
+
private readonly httpValidators;
|
|
2447
|
+
/**
|
|
2448
|
+
* Register a custom function for conditional expressions
|
|
2449
|
+
*
|
|
2450
|
+
* @param name - Unique identifier for the function
|
|
2451
|
+
* @param fn - Function that receives EvaluationContext and returns any value
|
|
2452
|
+
* @param options - Optional configuration for the function
|
|
2453
|
+
*/
|
|
2454
|
+
registerCustomFunction(name: string, fn: CustomFunction, options?: CustomFunctionOptions): void;
|
|
2455
|
+
/**
|
|
2456
|
+
* Get the scope of a registered custom function.
|
|
2457
|
+
*
|
|
2458
|
+
* @param name - The function name
|
|
2459
|
+
* @returns The function scope, or undefined if not registered
|
|
2460
|
+
*/
|
|
2461
|
+
getCustomFunctionScope(name: string): CustomFunctionScope | undefined;
|
|
2462
|
+
/**
|
|
2463
|
+
* Check if a custom function is field-scoped (no cross-field dependencies).
|
|
2464
|
+
*
|
|
2465
|
+
* @param name - The function name
|
|
2466
|
+
* @returns true if the function is field-scoped, false if form-scoped or not registered
|
|
2467
|
+
*/
|
|
2468
|
+
isFieldScopedFunction(name: string): boolean;
|
|
2469
|
+
/** Get all custom functions as an object */
|
|
2470
|
+
getCustomFunctions(): Record<string, CustomFunction>;
|
|
2471
|
+
/** Clear all custom functions and their scopes */
|
|
2472
|
+
clearCustomFunctions(): void;
|
|
2473
|
+
/**
|
|
2474
|
+
* Register a derivation function for value derivation logic.
|
|
2475
|
+
*
|
|
2476
|
+
* @param name - Unique identifier for the function
|
|
2477
|
+
* @param fn - Function that receives EvaluationContext and returns the derived value
|
|
2478
|
+
*/
|
|
2479
|
+
registerDerivationFunction(name: string, fn: CustomFunction): void;
|
|
2480
|
+
/** Get a derivation function by name */
|
|
2481
|
+
getDerivationFunction(name: string): CustomFunction | undefined;
|
|
2482
|
+
/** Get all derivation functions as an object */
|
|
2483
|
+
getDerivationFunctions(): Record<string, CustomFunction>;
|
|
2484
|
+
/**
|
|
2485
|
+
* Set derivation functions from a config object.
|
|
2486
|
+
* Only updates functions if their references have changed.
|
|
2487
|
+
*
|
|
2488
|
+
* @param derivations - Object mapping function names to derivation functions
|
|
2489
|
+
*/
|
|
2490
|
+
setDerivationFunctions(derivations: Record<string, CustomFunction> | undefined): void;
|
|
2491
|
+
/** Clear all derivation functions */
|
|
2492
|
+
clearDerivationFunctions(): void;
|
|
2493
|
+
/**
|
|
2494
|
+
* Register an async derivation function.
|
|
2495
|
+
*
|
|
2496
|
+
* @param name - Unique identifier for the function
|
|
2497
|
+
* @param fn - Function that receives EvaluationContext and returns Promise/Observable
|
|
2498
|
+
*/
|
|
2499
|
+
registerAsyncDerivationFunction(name: string, fn: AsyncDerivationFunction): void;
|
|
2500
|
+
/** Get an async derivation function by name */
|
|
2501
|
+
getAsyncDerivationFunction(name: string): AsyncDerivationFunction | undefined;
|
|
2502
|
+
/** Get all async derivation functions as an object */
|
|
2503
|
+
getAsyncDerivationFunctions(): Record<string, AsyncDerivationFunction>;
|
|
2504
|
+
/**
|
|
2505
|
+
* Set async derivation functions from a config object.
|
|
2506
|
+
* Only updates functions if their references have changed.
|
|
2507
|
+
*
|
|
2508
|
+
* @param fns - Object mapping function names to async derivation functions
|
|
2509
|
+
*/
|
|
2510
|
+
setAsyncDerivationFunctions(fns: Record<string, AsyncDerivationFunction> | undefined): void;
|
|
2511
|
+
/** Clear all async derivation functions */
|
|
2512
|
+
clearAsyncDerivationFunctions(): void;
|
|
2513
|
+
/**
|
|
2514
|
+
* Register an async condition function.
|
|
2515
|
+
*
|
|
2516
|
+
* @param name - Unique identifier for the function
|
|
2517
|
+
* @param fn - Function that receives EvaluationContext and returns Promise/Observable of boolean
|
|
2518
|
+
*/
|
|
2519
|
+
registerAsyncConditionFunction(name: string, fn: AsyncConditionFunction): void;
|
|
2520
|
+
/** Get an async condition function by name */
|
|
2521
|
+
getAsyncConditionFunction(name: string): AsyncConditionFunction | undefined;
|
|
2522
|
+
/** Get all async condition functions as an object */
|
|
2523
|
+
getAsyncConditionFunctions(): Record<string, AsyncConditionFunction>;
|
|
2524
|
+
/**
|
|
2525
|
+
* Set async condition functions from a config object.
|
|
2526
|
+
* Only updates functions if their references have changed.
|
|
2527
|
+
*
|
|
2528
|
+
* @param fns - Object mapping function names to async condition functions
|
|
2529
|
+
*/
|
|
2530
|
+
setAsyncConditionFunctions(fns: Record<string, AsyncConditionFunction> | undefined): void;
|
|
2531
|
+
/** Clear all async condition functions */
|
|
2532
|
+
clearAsyncConditionFunctions(): void;
|
|
2533
|
+
/**
|
|
2534
|
+
* Register a custom validator using Angular's public FieldContext API
|
|
2535
|
+
*
|
|
2536
|
+
* @param name - Unique identifier for the validator
|
|
2537
|
+
* @param fn - Validator function (ctx, params?) => ValidationError | ValidationError[] | null
|
|
2538
|
+
*/
|
|
2539
|
+
registerValidator(name: string, fn: CustomValidator): void;
|
|
2540
|
+
/** Get a validator by name */
|
|
2541
|
+
getValidator(name: string): CustomValidator | undefined;
|
|
2542
|
+
/** Clear all validators */
|
|
2543
|
+
clearValidators(): void;
|
|
2544
|
+
/**
|
|
2545
|
+
* Register an async validator using Angular's public validateAsync() API
|
|
2546
|
+
*
|
|
2547
|
+
* @param name - Unique identifier for the async validator
|
|
2548
|
+
* @param fn - Async validator function (ctx, params?) => Observable<ValidationError | ValidationError[] | null>
|
|
2549
|
+
*/
|
|
2550
|
+
registerAsyncValidator(name: string, fn: AsyncCustomValidator): void;
|
|
2551
|
+
/** Get an async validator by name */
|
|
2552
|
+
getAsyncValidator(name: string): AsyncCustomValidator | undefined;
|
|
2553
|
+
/** Clear all async validators */
|
|
2554
|
+
clearAsyncValidators(): void;
|
|
2555
|
+
/**
|
|
2556
|
+
* Register an HTTP validator configuration using Angular's public validateHttp() API
|
|
2557
|
+
*
|
|
2558
|
+
* @param name - Unique identifier for the HTTP validator
|
|
2559
|
+
* @param config - HTTP validator configuration object
|
|
2560
|
+
*/
|
|
2561
|
+
registerHttpValidator(name: string, config: HttpCustomValidator): void;
|
|
2562
|
+
/** Get an HTTP validator by name */
|
|
2563
|
+
getHttpValidator(name: string): HttpCustomValidator | undefined;
|
|
2564
|
+
/** Clear all HTTP validators */
|
|
2565
|
+
clearHttpValidators(): void;
|
|
2566
|
+
/**
|
|
2567
|
+
* Generic helper to set registry values only if they have changed
|
|
2568
|
+
*
|
|
2569
|
+
* @param registry - The Map to update
|
|
2570
|
+
* @param values - Object mapping keys to values
|
|
2571
|
+
*/
|
|
2572
|
+
private setRegistryIfChanged;
|
|
2573
|
+
/**
|
|
2574
|
+
* Set validators from a config object
|
|
2575
|
+
* Only updates validators if their references have changed
|
|
2576
|
+
*
|
|
2577
|
+
* @param validators - Object mapping validator names to validator functions
|
|
2578
|
+
*/
|
|
2579
|
+
setValidators(validators: Record<string, CustomValidator> | undefined): void;
|
|
2580
|
+
/**
|
|
2581
|
+
* Set async validators from a config object
|
|
2582
|
+
* Only updates validators if their references have changed
|
|
2583
|
+
*
|
|
2584
|
+
* @param asyncValidators - Object mapping validator names to async validator configs
|
|
2585
|
+
*/
|
|
2586
|
+
setAsyncValidators(asyncValidators: Record<string, AsyncCustomValidator> | undefined): void;
|
|
2587
|
+
/**
|
|
2588
|
+
* Set HTTP validators from a config object
|
|
2589
|
+
* Only updates validators if their references have changed
|
|
2590
|
+
*
|
|
2591
|
+
* @param httpValidators - Object mapping validator names to HTTP validator configs
|
|
2592
|
+
*/
|
|
2593
|
+
setHttpValidators(httpValidators: Record<string, HttpCustomValidator> | undefined): void;
|
|
2594
|
+
/** Clear everything (functions and all validators) */
|
|
2595
|
+
clearAll(): void;
|
|
2596
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FunctionRegistryService, never>;
|
|
2597
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<FunctionRegistryService>;
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
/** Registry service that provides access to the root form and its values. */
|
|
2601
|
+
declare class RootFormRegistryService {
|
|
2602
|
+
readonly formValue: Signal<Record<string, unknown>>;
|
|
2603
|
+
readonly rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>;
|
|
2604
|
+
constructor(formValue: Signal<Record<string, unknown>>, rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>);
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
|
|
2608
|
+
declare function applyValidators(configs: ValidatorConfig[], fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
|
|
2609
|
+
|
|
2610
|
+
/**
|
|
2611
|
+
* Create a dynamic value function from an expression string.
|
|
2612
|
+
* Uses secure AST-based parsing.
|
|
2613
|
+
*
|
|
2614
|
+
* @param expression The expression string to evaluate
|
|
2615
|
+
* @returns A LogicFn that evaluates the expression in the context of a field
|
|
2616
|
+
*/
|
|
2617
|
+
declare function createDynamicValueFunction<TValue, TReturn>(expression: string): LogicFn<TValue, TReturn>;
|
|
2618
|
+
|
|
2619
|
+
/** DI-scoped cache for dynamic value functions. */
|
|
2620
|
+
declare class DynamicValueFunctionCacheService {
|
|
2621
|
+
/** Cache for memoized dynamic value functions, keyed by expression string. */
|
|
2622
|
+
readonly dynamicValueFunctionCache: Map<string, LogicFn<unknown, unknown>>;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
interface FieldWithValidation {
|
|
2626
|
+
readonly required?: boolean;
|
|
2627
|
+
readonly email?: boolean;
|
|
2628
|
+
readonly min?: number;
|
|
2629
|
+
readonly max?: number;
|
|
2630
|
+
readonly minLength?: number;
|
|
2631
|
+
readonly maxLength?: number;
|
|
2632
|
+
readonly pattern?: string | RegExp;
|
|
2633
|
+
readonly validators?: ValidatorConfig[];
|
|
2634
|
+
readonly validationMessages?: ValidationMessages;
|
|
2635
|
+
readonly logic?: LogicConfig[];
|
|
2636
|
+
/** Shorthand for simple computed/derived fields. */
|
|
2637
|
+
readonly derivation?: string;
|
|
2638
|
+
readonly schemas?: SchemaApplicationConfig[];
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta, TNullable extends boolean = false> extends FieldDef<TProps, TMeta>, FieldWithValidation {
|
|
2642
|
+
value?: TNullable extends true ? boolean | null : boolean;
|
|
2643
|
+
/**
|
|
2644
|
+
* Placeholder text displayed when the field is empty.
|
|
2645
|
+
* Supports static strings, Observables, and Signals for dynamic content.
|
|
2646
|
+
*/
|
|
2647
|
+
placeholder?: DynamicText;
|
|
2648
|
+
required?: boolean;
|
|
2649
|
+
/**
|
|
2650
|
+
* Whether the field accepts `null` as a valid value.
|
|
2651
|
+
*
|
|
2652
|
+
* @default false
|
|
2653
|
+
*/
|
|
2654
|
+
nullable?: TNullable;
|
|
2655
|
+
}
|
|
2656
|
+
declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta, boolean>;
|
|
2657
|
+
type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
|
|
2658
|
+
type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
|
|
2659
|
+
|
|
2660
|
+
/**
|
|
2661
|
+
* Supported primitive value types for form fields.
|
|
2662
|
+
* This type represents all possible value types that can be used in form fields.
|
|
2663
|
+
*/
|
|
2664
|
+
type ValueType = string | number | boolean | Date | object | unknown[];
|
|
2665
|
+
interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TNullable extends boolean = false> extends FieldDef<TProps, TMeta>, FieldWithValidation {
|
|
2666
|
+
value?: TNullable extends true ? TValue | null : TValue;
|
|
2667
|
+
/**
|
|
2668
|
+
* Placeholder text displayed when the field is empty.
|
|
2669
|
+
* Supports static strings, Observables, and Signals for dynamic content.
|
|
2670
|
+
*/
|
|
2671
|
+
placeholder?: DynamicText;
|
|
2672
|
+
required?: boolean;
|
|
2673
|
+
/**
|
|
2674
|
+
* Whether the field accepts `null` as a valid value.
|
|
2675
|
+
*
|
|
2676
|
+
* @default false
|
|
2677
|
+
*/
|
|
2678
|
+
nullable?: TNullable;
|
|
2679
|
+
}
|
|
2680
|
+
declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
|
|
2681
|
+
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'valueType' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas' | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
|
|
2682
|
+
type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
|
|
2683
|
+
|
|
2684
|
+
/** Base interface for all form events in the dynamic form system. */
|
|
2685
|
+
interface FormEvent {
|
|
2686
|
+
/** Unique identifier for the event type used for filtering and subscriptions */
|
|
2687
|
+
readonly type: string;
|
|
2688
|
+
/**
|
|
2689
|
+
* Optional form value attached to the event when `withEventFormValue()` is enabled
|
|
2690
|
+
* or `options.emitFormValueOnEvents` is set to `true` in the form config.
|
|
2691
|
+
*/
|
|
2692
|
+
readonly formValue?: unknown;
|
|
2693
|
+
}
|
|
2694
|
+
/**
|
|
2695
|
+
* Type guard to check if a form event has a form value attached.
|
|
2696
|
+
*
|
|
2697
|
+
* @param event - The form event to check
|
|
2698
|
+
* @returns `true` if the event has a form value attached, narrowing the type
|
|
2699
|
+
*/
|
|
2700
|
+
declare function hasFormValue<T extends FormEvent>(event: T): event is T & {
|
|
2701
|
+
formValue: unknown;
|
|
2702
|
+
};
|
|
2703
|
+
/**
|
|
2704
|
+
* Constructor type for form event classes.
|
|
2705
|
+
*
|
|
2706
|
+
* @typeParam T - The form event type being constructed
|
|
2707
|
+
*/
|
|
2708
|
+
type FormEventConstructor<T extends FormEvent = FormEvent> = new (...args: any[]) => T;
|
|
2709
|
+
|
|
2710
|
+
declare class ComponentInitializedEvent implements FormEvent {
|
|
2711
|
+
componentType: 'dynamic-form' | 'page' | 'group' | 'array' | 'container';
|
|
2712
|
+
componentId: string;
|
|
2713
|
+
readonly type: "component-initialized";
|
|
2714
|
+
constructor(componentType: 'dynamic-form' | 'page' | 'group' | 'array' | 'container', componentId: string);
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
declare class EventBus {
|
|
2718
|
+
private readonly pipeline$;
|
|
2719
|
+
private readonly globalEmitFormValue;
|
|
2720
|
+
private readonly rootFormRegistry;
|
|
2721
|
+
private readonly formOptions;
|
|
2722
|
+
private readonly logger;
|
|
2723
|
+
events$: Observable<FormEvent>;
|
|
2724
|
+
/**
|
|
2725
|
+
* Dispatches a pre-created event instance directly.
|
|
2726
|
+
*
|
|
2727
|
+
* @param event - A FormEvent instance to dispatch
|
|
2728
|
+
*/
|
|
2729
|
+
dispatch(event: FormEvent): void;
|
|
2730
|
+
/**
|
|
2731
|
+
* Dispatches an event to all subscribers by instantiating the provided constructor.
|
|
2732
|
+
*
|
|
2733
|
+
* @param eventConstructor - Constructor function for the event to dispatch
|
|
2734
|
+
* @param args - Arguments to pass to the event constructor
|
|
2735
|
+
*/
|
|
2736
|
+
dispatch<T extends FormEventConstructor>(eventConstructor: T, ...args: ConstructorParameters<T>): void;
|
|
2737
|
+
/**
|
|
2738
|
+
* Dispatches a pre-created event instance directly.
|
|
2739
|
+
* Used internally by EventDispatcher to forward events into the bus.
|
|
2740
|
+
*
|
|
2741
|
+
* @internal
|
|
2742
|
+
*/
|
|
2743
|
+
emitInstance(event: FormEvent): void;
|
|
2744
|
+
/**
|
|
2745
|
+
* Shared emit path for both dispatch() and emitInstance().
|
|
2746
|
+
* Attaches form value if configured, then pushes to the pipeline.
|
|
2747
|
+
*/
|
|
2748
|
+
private emit;
|
|
2749
|
+
/**
|
|
2750
|
+
* Emits an event through the pipeline. Catches any synchronous exception that escapes
|
|
2751
|
+
* RxJS's own error handling so dispatch() callers are never disrupted by a failing subscriber.
|
|
2752
|
+
*/
|
|
2753
|
+
private safeEmit;
|
|
2754
|
+
/** Determines whether form value should be attached to events. */
|
|
2755
|
+
private shouldEmitFormValue;
|
|
2756
|
+
/**
|
|
2757
|
+
* Subscribes to events of a specific type.
|
|
2758
|
+
*
|
|
2759
|
+
* @param eventType - The type of event to subscribe to
|
|
2760
|
+
* @returns Observable that emits events of the specified type
|
|
2761
|
+
*/
|
|
2762
|
+
on<T extends FormEvent>(eventType: T['type']): Observable<T>;
|
|
2763
|
+
/**
|
|
2764
|
+
* Subscribes to events of multiple types.
|
|
2765
|
+
*
|
|
2766
|
+
* @param eventType - Array of event types to subscribe to
|
|
2767
|
+
* @returns Observable that emits events matching any of the specified types
|
|
2768
|
+
*/
|
|
2769
|
+
on<T extends FormEvent>(eventType: Array<T['type']>): Observable<T>;
|
|
2770
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<EventBus, never>;
|
|
2771
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<EventBus>;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
/** Context for token resolution in event args */
|
|
2775
|
+
interface TokenContext {
|
|
2776
|
+
/** Current field key */
|
|
2777
|
+
key?: string;
|
|
2778
|
+
/** Array index if field is inside an array */
|
|
2779
|
+
index?: number;
|
|
2780
|
+
/** Parent array field key if field is inside an array */
|
|
2781
|
+
arrayKey?: string;
|
|
2782
|
+
/** Current form value for complex indexing */
|
|
2783
|
+
formValue?: unknown;
|
|
2784
|
+
/** Template for array item creation */
|
|
2785
|
+
template?: unknown;
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* Resolves special tokens in event arguments to their actual values
|
|
2789
|
+
*
|
|
2790
|
+
* @param args - Array of arguments that may contain tokens
|
|
2791
|
+
* @param context - Context object containing token values
|
|
2792
|
+
* @returns Array with resolved values
|
|
2793
|
+
*/
|
|
2794
|
+
declare function resolveTokens(args: readonly (string | number | boolean | null | undefined)[], context: TokenContext): (string | number | boolean | null | undefined | unknown)[];
|
|
2795
|
+
|
|
2796
|
+
interface FieldWithHiddenLogic {
|
|
2797
|
+
hidden?: boolean;
|
|
2798
|
+
logic?: readonly (ContainerLogicConfig | LogicConfig)[];
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Applies hidden logic to a mapper's input record.
|
|
2802
|
+
*
|
|
2803
|
+
* @param inputs The mutable input record to potentially add `hidden` to
|
|
2804
|
+
* @param fieldDef The field definition containing `hidden` and `logic`
|
|
2805
|
+
* @param rootFormRegistry The root form registry for accessing form state
|
|
2806
|
+
*/
|
|
2807
|
+
declare function applyHiddenLogic(inputs: Record<string, unknown>, fieldDef: FieldWithHiddenLogic, rootFormRegistry: RootFormRegistryService): void;
|
|
2808
|
+
|
|
2809
|
+
/**
|
|
2810
|
+
* Maps an array field definition to component inputs.
|
|
2811
|
+
*
|
|
2812
|
+
* @param fieldDef The array field definition
|
|
2813
|
+
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
2814
|
+
*/
|
|
2815
|
+
declare function arrayFieldMapper(fieldDef: ArrayField): Signal<Record<string, unknown>>;
|
|
2816
|
+
|
|
2817
|
+
/**
|
|
2818
|
+
* Builds base input properties from a field definition.
|
|
2819
|
+
*
|
|
2820
|
+
* @param fieldDef The field definition to extract properties from
|
|
2821
|
+
* @param defaultProps Optional form-level default props to merge with field props
|
|
2822
|
+
* @returns Record of input names to values
|
|
2823
|
+
*/
|
|
2824
|
+
declare function buildBaseInputs(fieldDef: FieldDef<unknown>, defaultProps?: Record<string, unknown>): Record<string, unknown>;
|
|
2825
|
+
/**
|
|
2826
|
+
* Base field mapper that extracts common field properties into component inputs.
|
|
2827
|
+
*
|
|
2828
|
+
* @param fieldDef The field definition to map
|
|
2829
|
+
* @returns Signal containing Record of input names to values
|
|
2830
|
+
*/
|
|
2831
|
+
declare function baseFieldMapper(fieldDef: FieldDef<unknown>): Signal<Record<string, unknown>>;
|
|
2832
|
+
|
|
2833
|
+
/**
|
|
2834
|
+
* Maps a container field definition to component inputs.
|
|
2835
|
+
*
|
|
2836
|
+
* @param fieldDef The container field definition
|
|
2837
|
+
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
2838
|
+
*/
|
|
2839
|
+
declare function containerFieldMapper(fieldDef: ContainerField): Signal<Record<string, unknown>>;
|
|
2840
|
+
|
|
2841
|
+
/**
|
|
2842
|
+
* Maps a group field definition to component inputs.
|
|
2843
|
+
*
|
|
2844
|
+
* @param fieldDef The group field definition
|
|
2845
|
+
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
2846
|
+
*/
|
|
2847
|
+
declare function groupFieldMapper(fieldDef: GroupField): Signal<Record<string, unknown>>;
|
|
2848
|
+
|
|
2849
|
+
/**
|
|
2850
|
+
* Field signal context - the "nervous system" of the dynamic form.
|
|
2851
|
+
* Provided via FIELD_SIGNAL_CONTEXT injection token.
|
|
2852
|
+
*/
|
|
2853
|
+
interface FieldSignalContext<TModel extends Record<string, unknown> = Record<string, unknown>> {
|
|
2854
|
+
injector: Injector;
|
|
2855
|
+
value: WritableSignal<Partial<TModel> | undefined>;
|
|
2856
|
+
defaultValues: () => TModel;
|
|
2857
|
+
form: FieldTree<TModel>;
|
|
2858
|
+
/** Current page validity signal for paged forms. Used by next button to determine disabled state. */
|
|
2859
|
+
currentPageValid?: Signal<boolean>;
|
|
2860
|
+
}
|
|
2861
|
+
/**
|
|
2862
|
+
* Array context for fields rendered within arrays.
|
|
2863
|
+
* Provides position and parent array information for components inside array fields.
|
|
2864
|
+
*/
|
|
2865
|
+
interface ArrayContext {
|
|
2866
|
+
/** The key of the parent array field. */
|
|
2867
|
+
arrayKey: string;
|
|
2868
|
+
/**
|
|
2869
|
+
* The index of this item within the array.
|
|
2870
|
+
* Uses linkedSignal to automatically update when items are added/removed,
|
|
2871
|
+
* allowing index-dependent logic to react without component recreation.
|
|
2872
|
+
*/
|
|
2873
|
+
index: Signal<number>;
|
|
2874
|
+
/** The current form value for token resolution. */
|
|
2875
|
+
formValue: unknown;
|
|
2876
|
+
/** The array field definition. */
|
|
2877
|
+
field: FieldDef<unknown>;
|
|
2878
|
+
}
|
|
2879
|
+
/** Group context for fields rendered inside `group` containers. */
|
|
2880
|
+
interface GroupContext {
|
|
2881
|
+
/** Dot-separated path of group ancestors, including the current group's key (e.g. `'address'` or `'user.address'`). */
|
|
2882
|
+
groupPath: Signal<string>;
|
|
2883
|
+
}
|
|
2884
|
+
/** Mapper function that converts a field definition to component inputs. */
|
|
2885
|
+
type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
|
|
2886
|
+
|
|
2887
|
+
/** Maps a row field definition to container component inputs. */
|
|
2888
|
+
declare function rowFieldMapper(fieldDef: RowField): Signal<Record<string, unknown>>;
|
|
2889
|
+
|
|
2890
|
+
/**
|
|
2891
|
+
* Maps a page field definition to component inputs.
|
|
2892
|
+
*
|
|
2893
|
+
* @param fieldDef The page field definition
|
|
2894
|
+
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
2895
|
+
*/
|
|
2896
|
+
declare function pageFieldMapper(fieldDef: PageField): Signal<Record<string, unknown>>;
|
|
2897
|
+
|
|
2898
|
+
/**
|
|
2899
|
+
* Maps a text field definition to component inputs.
|
|
2900
|
+
*
|
|
2901
|
+
* @param fieldDef The text field definition
|
|
2902
|
+
* @returns Signal containing Record of input names to values for ngComponentOutlet
|
|
2903
|
+
*/
|
|
2904
|
+
declare function textFieldMapper(fieldDef: TextField): Signal<Record<string, unknown>>;
|
|
2905
|
+
|
|
2906
|
+
/**
|
|
2907
|
+
* Fields every addon-action context carries, regardless of whether the
|
|
2908
|
+
* addon is bound to a host field or rendered orphaned.
|
|
2909
|
+
*/
|
|
2910
|
+
interface AddonActionContextBase<TValue> {
|
|
2911
|
+
/**
|
|
2912
|
+
* Identity of the host field this addon is attached to. Empty strings
|
|
2913
|
+
* indicate the addon is rendered outside an active field context (rare).
|
|
2914
|
+
* Use {@link isFieldBoundContext} or the `form !== null` check to
|
|
2915
|
+
* narrow before relying on field identity.
|
|
2916
|
+
*/
|
|
2917
|
+
readonly field: {
|
|
2918
|
+
readonly key: string;
|
|
2919
|
+
readonly type: string;
|
|
2920
|
+
};
|
|
2921
|
+
/**
|
|
2922
|
+
* Current value of the host field at the time the action fires.
|
|
2923
|
+
* `undefined` when the host field has no value yet (initial render before
|
|
2924
|
+
* defaultValue resolution) or when the addon is rendered outside a field.
|
|
2925
|
+
*/
|
|
2926
|
+
readonly value: TValue | undefined;
|
|
2927
|
+
}
|
|
2928
|
+
/**
|
|
2929
|
+
* Field-bound variant — the addon is attached to a real field and has a
|
|
2930
|
+
* working value-writer. The common case for `pi-button` / `mat-button` /
|
|
2931
|
+
* `bs-button` / `ion-button` inside an adapter input field.
|
|
2932
|
+
*/
|
|
2933
|
+
interface FieldBoundAddonActionContext<TValue = unknown> extends AddonActionContextBase<TValue> {
|
|
2934
|
+
/** Read-only view of the host field's tree — same view wrappers receive. */
|
|
2935
|
+
readonly form: ReadonlyFieldTree<TValue>;
|
|
2936
|
+
/** Push a new value into the host field. */
|
|
2937
|
+
readonly setValue: (next: TValue) => void;
|
|
2938
|
+
}
|
|
2939
|
+
/**
|
|
2940
|
+
* Orphan variant — the addon is rendered outside any field component (rare;
|
|
2941
|
+
* happens when consumers compose `<df-addon-slot>` directly without a host
|
|
2942
|
+
* field). No writer is reachable; reading `value` may also yield `undefined`.
|
|
2943
|
+
*/
|
|
2944
|
+
interface OrphanAddonActionContext<TValue = unknown> extends AddonActionContextBase<TValue> {
|
|
2945
|
+
readonly form: null;
|
|
2946
|
+
/** Always absent in the orphan variant — distinguishes the union members. */
|
|
2947
|
+
readonly setValue?: undefined;
|
|
2948
|
+
}
|
|
2949
|
+
/** Context handed to inline addon actions and registered handler functions. */
|
|
2950
|
+
type AddonActionContext<TValue = unknown> = FieldBoundAddonActionContext<TValue> | OrphanAddonActionContext<TValue>;
|
|
2951
|
+
/** Type guard narrowing {@link AddonActionContext} to its field-bound variant. */
|
|
2952
|
+
declare function isFieldBoundContext<TValue>(ctx: AddonActionContext<TValue>): ctx is FieldBoundAddonActionContext<TValue>;
|
|
2953
|
+
/** Built-in preset actions shared across adapters. */
|
|
2954
|
+
type CommonAddonActionPreset = 'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility';
|
|
2955
|
+
/** Module-augmentable registry of adapter-specific preset names. */
|
|
2956
|
+
interface DynamicFormAddonActionPresetRegistry {
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Union of every preset name an addon may reference — universal presets
|
|
2960
|
+
* plus any contributed via {@link DynamicFormAddonActionPresetRegistry}.
|
|
2961
|
+
*/
|
|
2962
|
+
type AddonActionPreset = CommonAddonActionPreset | keyof DynamicFormAddonActionPresetRegistry | (string & {});
|
|
2963
|
+
/** Module-augmentable registry of user-defined named action handlers. */
|
|
2964
|
+
interface DynamicFormActionRegistry {
|
|
2965
|
+
}
|
|
2966
|
+
/**
|
|
2967
|
+
* Typed string handle to a registered action handler. The empty registry
|
|
2968
|
+
* resolves to `never`; augmenting `DynamicFormActionRegistry` populates the
|
|
2969
|
+
* union and unlocks autocomplete.
|
|
2970
|
+
*/
|
|
2971
|
+
type RegisteredActionRef = keyof DynamicFormActionRegistry;
|
|
2972
|
+
|
|
2973
|
+
/**
|
|
2974
|
+
* Minimal JSON Schema fragment used by the MCP server to surface an addon
|
|
2975
|
+
* kind's shape to AI form generators. Covers the common subset
|
|
2976
|
+
* (`type`/`properties`/`required`/`enum`/etc.) without pulling in a full JSON
|
|
2977
|
+
* Schema dependency. Extra keys are allowed via the `[key: string]` index so
|
|
2978
|
+
* vendor-specific annotations (`x-foo`, `markdownDescription`) still typecheck.
|
|
2979
|
+
*/
|
|
2980
|
+
interface AddonKindSchema {
|
|
2981
|
+
readonly $ref?: string;
|
|
2982
|
+
readonly $schema?: string;
|
|
2983
|
+
readonly title?: string;
|
|
2984
|
+
readonly description?: string;
|
|
2985
|
+
readonly type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null' | ReadonlyArray<'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'>;
|
|
2986
|
+
readonly enum?: ReadonlyArray<unknown>;
|
|
2987
|
+
readonly const?: unknown;
|
|
2988
|
+
readonly properties?: Readonly<Record<string, AddonKindSchema>>;
|
|
2989
|
+
readonly required?: ReadonlyArray<string>;
|
|
2990
|
+
readonly additionalProperties?: boolean | AddonKindSchema;
|
|
2991
|
+
readonly items?: AddonKindSchema | ReadonlyArray<AddonKindSchema>;
|
|
2992
|
+
readonly oneOf?: ReadonlyArray<AddonKindSchema>;
|
|
2993
|
+
readonly anyOf?: ReadonlyArray<AddonKindSchema>;
|
|
2994
|
+
readonly allOf?: ReadonlyArray<AddonKindSchema>;
|
|
2995
|
+
readonly not?: AddonKindSchema;
|
|
2996
|
+
readonly default?: unknown;
|
|
2997
|
+
readonly examples?: ReadonlyArray<unknown>;
|
|
2998
|
+
readonly minimum?: number;
|
|
2999
|
+
readonly maximum?: number;
|
|
3000
|
+
readonly minLength?: number;
|
|
3001
|
+
readonly maxLength?: number;
|
|
3002
|
+
readonly pattern?: string;
|
|
3003
|
+
readonly format?: string;
|
|
3004
|
+
/** Vendor extensions and unrecognised keywords pass through untyped. */
|
|
3005
|
+
readonly [key: string]: unknown;
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* Optional shape validator a kind ships to enforce its required fields at
|
|
3009
|
+
* runtime. Throws are caught by the validator and converted into lenient
|
|
3010
|
+
* warnings; the offending addon is dropped and the form keeps rendering.
|
|
3011
|
+
*/
|
|
3012
|
+
type AddonShapeValidator<T extends BaseAddon = BaseAddon> = (addon: T, fieldKey: string) => void;
|
|
3013
|
+
/** Per-kind registration metadata. */
|
|
3014
|
+
interface AddonKindDefinition<T extends BaseAddon = BaseAddon> {
|
|
3015
|
+
/** Discriminant matching `addon.kind`. */
|
|
3016
|
+
readonly kind: string;
|
|
3017
|
+
/** Lazy loader for the kind's renderer component. */
|
|
3018
|
+
readonly loadComponent: LazyComponentLoader;
|
|
3019
|
+
/** Optional shape validator — throw `DynamicFormError` to drop the addon with a warning. */
|
|
3020
|
+
readonly validate?: AddonShapeValidator<T>;
|
|
3021
|
+
/** Optional JSON Schema fragment for MCP / pre-flight tooling. */
|
|
3022
|
+
readonly schema?: AddonKindSchema;
|
|
3023
|
+
/**
|
|
3024
|
+
* Whether the kind survives `JSON.stringify` / `JSON.parse`. Kinds whose
|
|
3025
|
+
* shape includes a function payload (e.g., the built-in `'component'`
|
|
3026
|
+
* loader, inline `prime-button` `action` handlers) declare `jsonSafe: false`
|
|
3027
|
+
* so the validator drops them when the config was loaded from a JSON
|
|
3028
|
+
* source (`sanitizeFormConfig(config, { source: 'json' })`). Defaults to
|
|
3029
|
+
* `true` — kinds are JSON-safe unless explicitly opted out.
|
|
3030
|
+
*/
|
|
3031
|
+
readonly jsonSafe?: boolean;
|
|
3032
|
+
}
|
|
3033
|
+
/** Per-field-type addon-support metadata declared at field registration. */
|
|
3034
|
+
interface FieldAddonSupport {
|
|
3035
|
+
/** Slots a field of this type accepts. */
|
|
3036
|
+
readonly slots: readonly AddonSlot[];
|
|
3037
|
+
/** Optional whitelist of allowed addon kinds. Omitted = any registered kind. */
|
|
3038
|
+
readonly allowedKinds?: readonly string[];
|
|
3039
|
+
}
|
|
3040
|
+
/** Global registry of addon kinds. */
|
|
3041
|
+
declare const ADDON_KIND_REGISTRY: InjectionToken<Map<string, AddonKindDefinition<BaseAddon<AddonSlot>>>>;
|
|
3042
|
+
|
|
3043
|
+
/**
|
|
3044
|
+
* Map of named `TemplateRef`s collected by `<df-dynamic-form>` from
|
|
3045
|
+
* `<ng-template dfTemplate="...">` children projected into its content.
|
|
3046
|
+
* The directive selector is `dfTemplate` (see `DfTemplate`); the input
|
|
3047
|
+
* value is the name addons reference via `templateKey`.
|
|
3048
|
+
*/
|
|
3049
|
+
declare const DF_FIELD_TEMPLATES: InjectionToken<Signal<ReadonlyMap<string, TemplateRef<unknown>>>>;
|
|
3050
|
+
|
|
3051
|
+
interface FieldOption<T = unknown> {
|
|
3052
|
+
label: DynamicText;
|
|
3053
|
+
value: T;
|
|
3054
|
+
disabled?: boolean;
|
|
3055
|
+
[key: string]: unknown;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
/** Injection token for form-level default wrappers. */
|
|
3059
|
+
declare const DEFAULT_WRAPPERS: InjectionToken<Signal<readonly WrapperConfig[] | undefined>>;
|
|
3060
|
+
/** Injection token for providing field signal context to mappers and components. */
|
|
3061
|
+
declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
|
|
3062
|
+
/** Throwing accessor for {@link FIELD_SIGNAL_CONTEXT}. */
|
|
3063
|
+
declare function injectFieldSignalContext<TModel extends Record<string, unknown> = Record<string, unknown>>(): FieldSignalContext<TModel>;
|
|
3064
|
+
/** Injection token for providing array context metadata to mappers and components. */
|
|
3065
|
+
declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
|
|
3066
|
+
/** Injection token for providing group ancestry to mappers and components. */
|
|
3067
|
+
declare const GROUP_CONTEXT: InjectionToken<GroupContext>;
|
|
3068
|
+
/** Injection token for form-level default props. */
|
|
3069
|
+
declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
|
|
3070
|
+
/** Injection token for form-level default validation messages. */
|
|
3071
|
+
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
3072
|
+
/** Injection token for form-level options. */
|
|
3073
|
+
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
3074
|
+
/** Injection token for form-level external data. */
|
|
3075
|
+
declare const EXTERNAL_DATA: InjectionToken<Signal<Record<string, Signal<unknown>> | undefined>>;
|
|
3076
|
+
/**
|
|
3077
|
+
* Form-level DOM-id prefix, provided once at the DynamicForm level and read by
|
|
3078
|
+
* `mapFieldToInputs` as the outermost id segment: `{idPrefix}_{group}_{key}_{index}`.
|
|
3079
|
+
* Scopes ids to one form instance so duplicate forms on a page don't collide.
|
|
3080
|
+
* Inject `{ optional: true }` — absent under mock injectors / standalone usage.
|
|
3081
|
+
*/
|
|
3082
|
+
declare const FORM_ID_PREFIX: InjectionToken<Signal<string>>;
|
|
3083
|
+
|
|
3084
|
+
/** Defines how a field type handles form values and data collection. */
|
|
3085
|
+
type ValueHandlingMode = 'include' | 'exclude' | 'flatten';
|
|
3086
|
+
/** Semantic grouping of interchangeable field UI alternatives. */
|
|
3087
|
+
type FieldScope = 'boolean' | 'single-select' | 'multi-select' | 'text-input' | 'numeric' | 'date';
|
|
3088
|
+
/** Mapped component inputs that must be available before a field component is instantiated. */
|
|
3089
|
+
type RenderReadyInput = 'field' | (string & {});
|
|
3090
|
+
/**
|
|
3091
|
+
* Configuration interface for registering custom field types.
|
|
3092
|
+
*
|
|
3093
|
+
* @typeParam T - The field definition type this field type handles
|
|
3094
|
+
*/
|
|
3095
|
+
interface FieldTypeDefinition<T extends FieldDef<any> = any> {
|
|
3096
|
+
/** Unique identifier for the field type */
|
|
3097
|
+
name: string;
|
|
3098
|
+
/** Field definition type marker (internal use) */
|
|
3099
|
+
_fieldDef?: T;
|
|
3100
|
+
/**
|
|
3101
|
+
* Function to load the component (supports lazy loading).
|
|
3102
|
+
* Returns a Promise that resolves to the component class or module with default export.
|
|
3103
|
+
*/
|
|
3104
|
+
loadComponent?: LazyComponentLoader;
|
|
3105
|
+
/** Mapper function that converts field definition to component bindings. */
|
|
3106
|
+
mapper?: MapperFn<T>;
|
|
3107
|
+
/** How this field type handles form values and data collection (defaults to 'include') */
|
|
3108
|
+
valueHandling?: ValueHandlingMode;
|
|
3109
|
+
/** List of prop keys to forward to the meta object. */
|
|
3110
|
+
propsToMeta?: string[];
|
|
3111
|
+
/** Semantic scope for tooling to discover interchangeable field alternatives */
|
|
3112
|
+
scope?: FieldScope | FieldScope[];
|
|
3113
|
+
/** Mapped component inputs that must exist before the renderer instantiates the component */
|
|
3114
|
+
renderReadyWhen?: readonly RenderReadyInput[];
|
|
3115
|
+
/**
|
|
3116
|
+
* Declares which addon slots this field accepts and (optionally) the
|
|
3117
|
+
* subset of kinds allowed.
|
|
3118
|
+
*/
|
|
3119
|
+
addons?: FieldAddonSupport;
|
|
3120
|
+
}
|
|
3121
|
+
/** Injection token for the global field type registry. */
|
|
3122
|
+
declare const FIELD_REGISTRY: InjectionToken<Map<string, FieldTypeDefinition<any>>>;
|
|
3123
|
+
/**
|
|
3124
|
+
* Gets the value handling mode for a specific field type from the registry.
|
|
3125
|
+
*
|
|
3126
|
+
* @param fieldType - The field type identifier
|
|
3127
|
+
* @param registry - The field type registry
|
|
3128
|
+
* @returns The value handling mode ('include' is the default if not specified)
|
|
3129
|
+
*/
|
|
3130
|
+
declare function getFieldValueHandling(fieldType: string, registry: Map<string, FieldTypeDefinition>): ValueHandlingMode;
|
|
3131
|
+
|
|
3132
|
+
/** Form mode enumeration distinguishing between paged and non-paged forms */
|
|
3133
|
+
type FormMode = 'paged' | 'non-paged';
|
|
3134
|
+
/** Result of form mode detection with validation details */
|
|
3135
|
+
interface FormModeDetectionResult {
|
|
3136
|
+
/** The detected form mode */
|
|
3137
|
+
mode: FormMode;
|
|
3138
|
+
/** Whether the form configuration is valid for the detected mode */
|
|
3139
|
+
isValid: boolean;
|
|
3140
|
+
/** Array of validation errors if form is invalid */
|
|
3141
|
+
errors: string[];
|
|
3142
|
+
}
|
|
3143
|
+
/**
|
|
3144
|
+
* Type guard for paged form configurations
|
|
3145
|
+
* A paged form has ALL root-level fields of type 'page'
|
|
3146
|
+
*/
|
|
3147
|
+
declare function isPagedForm<TFields extends RegisteredFieldTypes[]>(fields: TFields): boolean;
|
|
3148
|
+
/**
|
|
3149
|
+
* Type guard for non-paged form configurations
|
|
3150
|
+
* A non-paged form has NO page fields at any level
|
|
3151
|
+
*/
|
|
3152
|
+
declare function isNonPagedForm<TFields extends RegisteredFieldTypes[]>(fields: TFields): boolean;
|
|
3153
|
+
/**
|
|
3154
|
+
* Detects the form mode and validates the configuration
|
|
3155
|
+
*
|
|
3156
|
+
* @param fields The form field definitions
|
|
3157
|
+
* @returns Detection result with mode and validation status
|
|
3158
|
+
*/
|
|
3159
|
+
declare function detectFormMode<TFields extends RegisteredFieldTypes[]>(fields: TFields): FormModeDetectionResult;
|
|
3160
|
+
/** Type predicate for valid paged form configurations */
|
|
3161
|
+
declare function isValidPagedForm<TFields extends RegisteredFieldTypes[]>(fields: TFields): boolean;
|
|
3162
|
+
/** Type predicate for valid non-paged form configurations */
|
|
3163
|
+
declare function isValidNonPagedForm<TFields extends RegisteredFieldTypes[]>(fields: TFields): boolean;
|
|
3164
|
+
|
|
3165
|
+
/**
|
|
3166
|
+
* Type guard to check if a field definition has child fields.
|
|
3167
|
+
* This is a looser check that works with FieldDef<unknown> without requiring RegisteredFieldTypes.
|
|
3168
|
+
*/
|
|
3169
|
+
declare function hasChildFields<T>(field: FieldDef<T>): field is FieldDef<T> & {
|
|
3170
|
+
fields: FieldDef<unknown>[] | Record<string, FieldDef<unknown>>;
|
|
3171
|
+
};
|
|
3172
|
+
/**
|
|
3173
|
+
* Type guard to check if a field is a container field (page, row, group, or array)
|
|
3174
|
+
* Container fields have a 'fields' property and don't contribute values directly.
|
|
3175
|
+
* This overload works with RegisteredFieldTypes for full type narrowing.
|
|
3176
|
+
*/
|
|
3177
|
+
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
3178
|
+
/**
|
|
3179
|
+
* Type guard to check if a field is a container field (page, row, group, or array).
|
|
3180
|
+
* This overload works with any FieldDef for looser type checking.
|
|
3181
|
+
*/
|
|
3182
|
+
declare function isContainerField(field: FieldDef<unknown>): boolean;
|
|
3183
|
+
/**
|
|
3184
|
+
* Type guard to check if a field is a leaf field (value or display field)
|
|
3185
|
+
* Leaf fields don't have children and either contribute values or display content
|
|
3186
|
+
*/
|
|
3187
|
+
declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
|
|
3188
|
+
/**
|
|
3189
|
+
* Type guard to check if a field has a value property (value-bearing field)
|
|
3190
|
+
* These fields contribute to the form value output
|
|
3191
|
+
* Note: Using `unknown` in the Extract condition to match any value type
|
|
3192
|
+
*/
|
|
3193
|
+
declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
|
|
3194
|
+
value: unknown;
|
|
3195
|
+
}>;
|
|
3196
|
+
/**
|
|
3197
|
+
* Type guard to check if a field is excluded from form values (display-only field)
|
|
3198
|
+
* Currently this includes text fields and any other fields without a value property
|
|
3199
|
+
*/
|
|
3200
|
+
declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
|
|
3201
|
+
|
|
3202
|
+
/** Helper types for creating type-safe field configurations with proper nesting constraints */
|
|
3203
|
+
|
|
3204
|
+
/**
|
|
3205
|
+
* Type helper for accessing nested field paths safely
|
|
3206
|
+
* This allows accessing child paths while maintaining some type safety
|
|
3207
|
+
*/
|
|
3208
|
+
type FieldPathAccess<TValue> = {
|
|
3209
|
+
[K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
|
|
3210
|
+
};
|
|
3211
|
+
|
|
3212
|
+
/** Configuration for when validation runs based on a field's reactive state. */
|
|
3213
|
+
interface ValidationExecutionConfig {
|
|
3214
|
+
/**
|
|
3215
|
+
* Whether validators run while a field is hidden (statically, via `hidden`
|
|
3216
|
+
* logic, or because any ancestor is hidden).
|
|
3217
|
+
*/
|
|
3218
|
+
readonly validateWhenHidden?: boolean;
|
|
3219
|
+
}
|
|
3220
|
+
/** Fully resolved validation execution config — every property defined. */
|
|
3221
|
+
type ResolvedValidationExecutionConfig = Required<ValidationExecutionConfig>;
|
|
3222
|
+
|
|
3223
|
+
/**
|
|
3224
|
+
* Configuration for excluding field values from form submission output
|
|
3225
|
+
* based on their reactive state (hidden, disabled, readonly).
|
|
3226
|
+
*/
|
|
3227
|
+
interface ValueExclusionConfig {
|
|
3228
|
+
/**
|
|
3229
|
+
* Whether to exclude the value of hidden fields from submission output.
|
|
3230
|
+
*
|
|
3231
|
+
* @remarks
|
|
3232
|
+
* This does NOT affect HiddenField (`type: 'hidden'`) — those fields
|
|
3233
|
+
* store values without UI and their `hidden()` state is always `false`.
|
|
3234
|
+
*/
|
|
3235
|
+
readonly excludeValueIfHidden?: boolean;
|
|
3236
|
+
/** Whether to exclude the value of disabled fields from submission output. */
|
|
3237
|
+
readonly excludeValueIfDisabled?: boolean;
|
|
3238
|
+
/** Whether to exclude the value of readonly fields from submission output. */
|
|
3239
|
+
readonly excludeValueIfReadonly?: boolean;
|
|
3240
|
+
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Fully resolved exclusion config with all properties required.
|
|
3243
|
+
* Produced by the 3-tier resolution: `field ?? form ?? global`.
|
|
3244
|
+
*/
|
|
3245
|
+
type ResolvedValueExclusionConfig = Required<ValueExclusionConfig>;
|
|
3246
|
+
|
|
3247
|
+
/**
|
|
3248
|
+
* Pipe that handles dynamic text resolution with support for static strings,
|
|
3249
|
+
* Observables, and Signals.
|
|
3250
|
+
*/
|
|
3251
|
+
declare class DynamicTextPipe implements PipeTransform {
|
|
3252
|
+
private readonly injector;
|
|
3253
|
+
/**
|
|
3254
|
+
* Transforms dynamic text input into a resolved string value
|
|
3255
|
+
*
|
|
3256
|
+
* @param value - The dynamic text value to resolve
|
|
3257
|
+
* @returns The resolved string value as an Observable
|
|
3258
|
+
*/
|
|
3259
|
+
transform(value: DynamicText | undefined): Observable<string>;
|
|
3260
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DynamicTextPipe, never>;
|
|
3261
|
+
static ɵpipe: i0.ɵɵPipeDeclaration<DynamicTextPipe, "dynamicText", true>;
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
/**
|
|
3265
|
+
* Injection token for globally enabling form value emission on events.
|
|
3266
|
+
*
|
|
3267
|
+
* @internal
|
|
3268
|
+
*/
|
|
3269
|
+
declare const EMIT_FORM_VALUE_ON_EVENTS: InjectionToken<boolean>;
|
|
3270
|
+
|
|
3271
|
+
/** Injection token for the dynamic forms logger. */
|
|
3272
|
+
declare const DynamicFormLogger: InjectionToken<Logger>;
|
|
3273
|
+
|
|
3274
|
+
/**
|
|
3275
|
+
* No-operation logger implementation.
|
|
3276
|
+
* All methods are no-ops - used in production by default.
|
|
3277
|
+
*/
|
|
3278
|
+
declare class NoopLogger implements Logger {
|
|
3279
|
+
debug(): void;
|
|
3280
|
+
info(): void;
|
|
3281
|
+
warn(): void;
|
|
3282
|
+
error(): void;
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
/**
|
|
3286
|
+
* Applies meta attributes to a DOM element and tracks which attributes were applied.
|
|
3287
|
+
*
|
|
3288
|
+
* @param element - The DOM element to apply attributes to
|
|
3289
|
+
* @param meta - The meta object containing attributes to apply
|
|
3290
|
+
* @param previouslyApplied - Set of attribute names that were previously applied
|
|
3291
|
+
* @returns A new Set containing the attribute names that were applied
|
|
3292
|
+
*/
|
|
3293
|
+
declare function applyMetaToElement(element: Element, meta: FieldMeta | undefined, previouslyApplied: Set<string>): Set<string>;
|
|
3294
|
+
|
|
3295
|
+
/**
|
|
3296
|
+
* Injection token for the deprecation warning tracker.
|
|
3297
|
+
* Provided at form component level for instance-scoped tracking of deprecated API usages
|
|
3298
|
+
* (keyed by deprecation id) so the same warning fires once per form rather than every render.
|
|
3299
|
+
*
|
|
3300
|
+
* @internal
|
|
3301
|
+
*/
|
|
3302
|
+
declare const DEPRECATION_WARNING_TRACKER: InjectionToken<WarningTracker>;
|
|
3303
|
+
|
|
3304
|
+
/** Options for derivedFromDeferred. */
|
|
3305
|
+
interface DerivedFromDeferredOptions<R> {
|
|
3306
|
+
initialValue: R;
|
|
3307
|
+
injector: Injector;
|
|
3308
|
+
}
|
|
3309
|
+
/** Type helper to extract the value type from a Signal. */
|
|
3310
|
+
type SignalValue<T> = T extends Signal<infer V> ? V : never;
|
|
3311
|
+
/** Type helper to map an array of Signals to an array of their value types. */
|
|
3312
|
+
type SignalValues<T extends readonly Signal<unknown>[]> = {
|
|
3313
|
+
[K in keyof T]: SignalValue<T[K]>;
|
|
3314
|
+
};
|
|
3315
|
+
/** Type helper to map an object of Signals to an object of their value types. */
|
|
3316
|
+
type SignalObjectValues<T extends Record<string, Signal<unknown>>> = {
|
|
3317
|
+
[K in keyof T]: SignalValue<T[K]>;
|
|
3318
|
+
};
|
|
3319
|
+
/**
|
|
3320
|
+
* Like derivedFrom from ngxtension, but uses defer() to avoid input availability issues.
|
|
3321
|
+
* This ensures toObservable is called at subscription time, not at field initialization.
|
|
3322
|
+
*/
|
|
3323
|
+
declare function derivedFromDeferred<T, R>(source: Signal<T>, operator: OperatorFunction<T, R>, options: DerivedFromDeferredOptions<R>): Signal<R>;
|
|
3324
|
+
declare function derivedFromDeferred<T extends readonly Signal<unknown>[], R>(sources: [...T], operator: OperatorFunction<SignalValues<T>, R>, options: DerivedFromDeferredOptions<R>): Signal<R>;
|
|
3325
|
+
declare function derivedFromDeferred<T extends Record<string, Signal<unknown>>, R>(sources: T, operator: OperatorFunction<SignalObjectValues<T>, R>, options: DerivedFromDeferredOptions<R>): Signal<R>;
|
|
3326
|
+
|
|
3327
|
+
/**
|
|
3328
|
+
* Converts DynamicText (string | Observable | Signal) to Observable<string>
|
|
3329
|
+
* Unifies all three types into a consistent Observable stream
|
|
3330
|
+
*
|
|
3331
|
+
* @param value - The dynamic text value to convert
|
|
3332
|
+
* @param injector - Optional injector for signal conversion
|
|
3333
|
+
* @returns Observable<string> - The value as an observable stream
|
|
3334
|
+
*/
|
|
3335
|
+
declare function dynamicTextToObservable(value: DynamicText | undefined, injector?: Injector): Observable<string>;
|
|
3336
|
+
|
|
3337
|
+
/** Normalises a {@link DynamicValue} into a `Signal<T>`. */
|
|
3338
|
+
declare function resolveDynamicValue<T>(value: DynamicValue<T> | undefined, fallback: T, injector?: Injector): Signal<T>;
|
|
3339
|
+
|
|
3340
|
+
/**
|
|
3341
|
+
* Generates CSS class string for responsive grid layout based on field column configuration.
|
|
3342
|
+
*
|
|
3343
|
+
* @param fieldDef - Field definition containing column configuration
|
|
3344
|
+
* @returns CSS class string for grid layout, empty if no valid column configuration
|
|
3345
|
+
*/
|
|
3346
|
+
declare function getGridClassString(fieldDef: FieldDef<unknown>): string;
|
|
3347
|
+
/**
|
|
3348
|
+
* Builds a combined className string from a field definition.
|
|
3349
|
+
*
|
|
3350
|
+
* @param fieldDef - Field definition containing className and col properties
|
|
3351
|
+
* @returns Combined class string, or undefined if no classes
|
|
3352
|
+
*/
|
|
3353
|
+
declare function buildClassName(fieldDef: FieldDef<unknown>): string | undefined;
|
|
3354
|
+
|
|
3355
|
+
/**
|
|
3356
|
+
* Injection token for configuring the initialization timeout in milliseconds.
|
|
3357
|
+
* Defaults to 10 seconds. When the timeout is reached, a warning is logged
|
|
3358
|
+
* and (initialized) emits true as a best-effort fallback.
|
|
3359
|
+
*/
|
|
3360
|
+
declare const INITIALIZATION_TIMEOUT_MS: InjectionToken<number>;
|
|
3361
|
+
/** Creates an observable that tracks component initialization progress. */
|
|
3362
|
+
declare function createInitializationTracker(eventBus: EventBus, expectedCount: number): Observable<boolean>;
|
|
3363
|
+
/** Options for setting up initialization tracking. */
|
|
3364
|
+
interface InitializationTrackingOptions {
|
|
3365
|
+
eventBus: EventBus;
|
|
3366
|
+
totalComponentsCount: Signal<number>;
|
|
3367
|
+
injector: Injector;
|
|
3368
|
+
componentId: string;
|
|
3369
|
+
}
|
|
3370
|
+
/**
|
|
3371
|
+
* Creates an observable that tracks when all form components are initialized.
|
|
3372
|
+
* Uses shareReplay({ bufferSize: 1, refCount: false }) to ensure exactly one emission
|
|
3373
|
+
* that can be received by late subscribers and keeps the subscription alive.
|
|
3374
|
+
*/
|
|
3375
|
+
declare function setupInitializationTracking(options: InitializationTrackingOptions): Observable<boolean>;
|
|
3376
|
+
|
|
3377
|
+
/**
|
|
3378
|
+
* Interpolates {{param}} placeholders in message with error values
|
|
3379
|
+
*
|
|
3380
|
+
* @param message - Message template with {{param}} placeholders
|
|
3381
|
+
* @param error - Validation error containing parameter values
|
|
3382
|
+
* @returns Message with interpolated parameters
|
|
3383
|
+
*/
|
|
3384
|
+
declare function interpolateParams(message: string, error: ValidationError): string;
|
|
3385
|
+
|
|
3386
|
+
/**
|
|
3387
|
+
* Native JavaScript utility functions to replace lodash-es
|
|
3388
|
+
* These are simple implementations for common object manipulation patterns
|
|
3389
|
+
*/
|
|
3390
|
+
/**
|
|
3391
|
+
* Performs a deep equality comparison between two values.
|
|
3392
|
+
*
|
|
3393
|
+
* @param a - First value
|
|
3394
|
+
* @param b - Second value
|
|
3395
|
+
* @returns true if values are deeply equal
|
|
3396
|
+
*/
|
|
3397
|
+
declare function isEqual(a: unknown, b: unknown): boolean;
|
|
3398
|
+
/**
|
|
3399
|
+
* Creates a new object without the specified keys
|
|
3400
|
+
* Native replacement for lodash omit()
|
|
3401
|
+
*
|
|
3402
|
+
* @param obj - Source object
|
|
3403
|
+
* @param keys - Keys to omit
|
|
3404
|
+
* @returns New object without specified keys
|
|
3405
|
+
*/
|
|
3406
|
+
declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
|
|
3407
|
+
/**
|
|
3408
|
+
* Creates an object from an array, keyed by a specified property
|
|
3409
|
+
* Native replacement for lodash keyBy()
|
|
3410
|
+
*
|
|
3411
|
+
* @param array - Source array
|
|
3412
|
+
* @param key - Property to use as key
|
|
3413
|
+
* @returns Object keyed by the specified property
|
|
3414
|
+
*/
|
|
3415
|
+
declare function keyBy<T extends object>(array: T[], key: keyof T): Record<string, T>;
|
|
3416
|
+
/**
|
|
3417
|
+
* Maps object values through a transformation function
|
|
3418
|
+
* Native replacement for lodash mapValues()
|
|
3419
|
+
*
|
|
3420
|
+
* @param obj - Source object
|
|
3421
|
+
* @param fn - Transformation function
|
|
3422
|
+
* @returns New object with transformed values
|
|
3423
|
+
*/
|
|
3424
|
+
declare function mapValues<T, U>(obj: Record<string, T>, fn: (value: T, key: string) => U): Record<string, U>;
|
|
3425
|
+
/**
|
|
3426
|
+
* Deep-merges `value` into `defaults`, producing a new object that contains every
|
|
3427
|
+
* key from `defaults` plus any overrides from `value`. Plain-object values at
|
|
3428
|
+
* matching keys are merged recursively; primitives, dates, and class instances
|
|
3429
|
+
* in `value` replace the corresponding default wholesale.
|
|
3430
|
+
*/
|
|
3431
|
+
declare function deepMergeDefaults<T extends Record<string, unknown>>(defaults: T, value: Record<string, unknown> | null | undefined): T;
|
|
3432
|
+
interface MemoizeOptions<TFunc extends (...args: never[]) => unknown> {
|
|
3433
|
+
/** Optional function to generate cache key from arguments */
|
|
3434
|
+
resolver?: (...args: Parameters<TFunc>) => string | number;
|
|
3435
|
+
/** Maximum number of entries to keep in cache. Uses LRU eviction when exceeded. Defaults to 100. */
|
|
3436
|
+
maxSize?: number;
|
|
3437
|
+
}
|
|
3438
|
+
/**
|
|
3439
|
+
* Memoizes a function with LRU cache eviction and optional custom cache key resolver.
|
|
3440
|
+
*
|
|
3441
|
+
* @param fn - Function to memoize
|
|
3442
|
+
* @param resolverOrOptions - Optional key resolver function or options object
|
|
3443
|
+
* @returns Memoized function
|
|
3444
|
+
*/
|
|
3445
|
+
declare function memoize<TFunc extends (...args: any[]) => any>(fn: TFunc, resolverOrOptions?: ((...args: Parameters<TFunc>) => string | number) | MemoizeOptions<TFunc>): TFunc;
|
|
3446
|
+
/**
|
|
3447
|
+
* Normalizes a fields collection to an array.
|
|
3448
|
+
* Handles both array format and object format (keyed by field key).
|
|
3449
|
+
*
|
|
3450
|
+
* @param fields - Fields as array or object
|
|
3451
|
+
* @returns Fields as array
|
|
3452
|
+
*/
|
|
3453
|
+
declare function normalizeFieldsArray<T>(fields: readonly T[] | Record<string, T>): T[];
|
|
3454
|
+
/**
|
|
3455
|
+
* Gets the keys that differ between two objects.
|
|
3456
|
+
*
|
|
3457
|
+
* @param previous - Previous object state
|
|
3458
|
+
* @param current - Current object state
|
|
3459
|
+
* @returns Set of keys that have different values
|
|
3460
|
+
*/
|
|
3461
|
+
declare function getChangedKeys(previous: Record<string, unknown> | null | undefined, current: Record<string, unknown> | null | undefined): Set<string>;
|
|
3462
|
+
/** Compile-time exhaustiveness check for discriminated unions. */
|
|
3463
|
+
declare function assertNever(value: never): never;
|
|
3464
|
+
|
|
3465
|
+
/**
|
|
3466
|
+
* Wraps a `Resource` so that the previous resolved value is preserved while the resource
|
|
3467
|
+
* is loading or reloading. This prevents UI flicker when params change and a new request
|
|
3468
|
+
* is in-flight — the consumer sees the stale value with a `loading`/`reloading` status
|
|
3469
|
+
* instead of `undefined`.
|
|
3470
|
+
*/
|
|
3471
|
+
declare function withPreviousValue<T>(input: Resource<T>): Resource<T>;
|
|
3472
|
+
|
|
3473
|
+
/**
|
|
3474
|
+
* Safely reads `pathKeys` from a FieldContext.
|
|
3475
|
+
* Returns an empty array if `pathKeys` is not available (e.g., in tests or older Angular versions).
|
|
3476
|
+
*/
|
|
3477
|
+
declare function safeReadPathKeys(fieldContext: FieldContext<unknown>): readonly string[];
|
|
3478
|
+
|
|
3479
|
+
/**
|
|
3480
|
+
* Serializes a value to a deterministic string for cache key generation.
|
|
3481
|
+
* Unlike JSON.stringify, this sorts object keys to ensure consistent output
|
|
3482
|
+
* regardless of property insertion order.
|
|
3483
|
+
*/
|
|
3484
|
+
declare function stableStringify(value: unknown): string;
|
|
3485
|
+
|
|
3486
|
+
/** Input shape passed to a wrapper component via the `fieldInputs` input. */
|
|
3487
|
+
interface WrapperFieldInputs {
|
|
3488
|
+
readonly key: string;
|
|
3489
|
+
/** Field-type discriminant (mirrors `FieldDef.type`) — useful for kind-aware addons. */
|
|
3490
|
+
readonly type?: string;
|
|
3491
|
+
readonly label?: string;
|
|
3492
|
+
readonly placeholder?: string;
|
|
3493
|
+
readonly className?: string;
|
|
3494
|
+
readonly props?: Record<string, unknown>;
|
|
3495
|
+
readonly validationMessages?: Record<string, string>;
|
|
3496
|
+
readonly defaultValidationMessages?: Record<string, string>;
|
|
3497
|
+
readonly field?: ReadonlyFieldTree;
|
|
3498
|
+
/**
|
|
3499
|
+
* Write a value into the wrapped field's tree. Provided by `buildFieldInputs`
|
|
3500
|
+
* when a `FieldTree` is present. Addons (e.g., `prime-button` presets) use this
|
|
3501
|
+
* to mutate the host field; wrappers should treat the field as read-only.
|
|
3502
|
+
*/
|
|
3503
|
+
readonly setValue?: (next: unknown) => void;
|
|
3504
|
+
readonly [key: string]: unknown;
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
export { ADDON_KIND_REGISTRY, ARRAY_CONTEXT, AsyncConditionFunctionCacheService, ComponentInitializedEvent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DEPRECATION_WARNING_TRACKER, DF_FIELD_TEMPLATES, DynamicFormError, DynamicFormLogger, DynamicTextPipe, DynamicValueFunctionCacheService, EMIT_FORM_VALUE_ON_EVENTS, EXTERNAL_DATA, Evaluator, EventBus, ExpressionParser, ExpressionParserError, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_ID_PREFIX, FORM_OPTIONS, FieldContextRegistryService, FunctionRegistryService, GROUP_CONTEXT, HTTP_CONDITION_CACHE, HttpConditionCache, HttpConditionFunctionCacheService, INITIALIZATION_TIMEOUT_MS, LogicFunctionCacheService, NoopLogger, Parser, READONLY_FIELD_TREE_CACHE, RootFormRegistryService, TokenType, Tokenizer, WRAPPER_AUTO_ASSOCIATIONS, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, applyHiddenLogic, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, assertNever, baseFieldMapper, buildBaseInputs, buildClassName, compareValues, containerFieldMapper, createAsyncConditionLogicFunction, createDebouncedLogicFunction, createDebouncedResourceLogicFn, createDefaultDerivationLogConfig, createDynamicValueFunction, createFormFieldStateMap, createHttpConditionLogicFunction, createInitializationTracker, createLogicFunction, createWarningTracker, deepMergeDefaults, derivedFromDeferred, detectFormMode, dynamicTextToObservable, evaluateCondition, evaluateHttpValidationResponse, evaluateNonFieldDisabled, evaluateNonFieldHidden, extractExpressionDependencies, extractStringDependencies, getArrayLength, getChangedKeys, getFieldValueHandling, getGridClassString, getNestedValue, groupFieldMapper, hasChildFields, hasCrossFieldWhenCondition, hasFormValue, hasNestedProperty, hasTargetProperty, injectFieldSignalContext, interpolateParams, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isCrossFieldBuiltInValidator, isCrossFieldExpression, isCrossFieldSchema, isCrossFieldStateLogic, isCrossFieldValidator, isDerivationLogicConfig, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isFunctionHttpValidator, isGroupField, isHiddenField, isLeafField, isNonPagedForm, isPageField, isPagedForm, isResourceBasedValidator, isRowField, isSimplifiedArrayField, isStateLogicConfig, isValidNonPagedForm, isValidPagedForm, isValueBearingField, isValueField, isWrapperTypeDefinition, keyBy, mapValues, memoize, normalizeFieldsArray, omit, pageFieldMapper, readFieldStateInfo, resolveDynamicValue, resolveHttpRequest, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, safeReadPathKeys, setupInitializationTracking, shouldLog, stableStringify, textFieldMapper, toReadonlyFieldTree, toReadonlyFieldTreeCached, validatePageNesting, withPreviousValue, writeToFieldValue };
|
|
3508
|
+
export type { ASTNode, AddonActionContext, AddonActionPreset, AddonKindDefinition, AddonKindSchema, AddonShapeValidator, AddonSlot, AndCondition, AnyAddon, ArrayAllowedChildren, ArrayButtonConfig, ArrayComponent, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemDefinition, ArrayItemTemplate, ArrayLiteralNode, ArrowFunctionNode, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseAddon, BaseCheckedField, BaseValidatorConfig, BaseValueField, BinaryOpNode, BuiltInValidatorConfig, ButtonLogicContext, CallExpressionNode, CheckedFieldComponent, CommonAddonActionPreset, CommonAddonSlot, ComparisonOperator, ComponentAddon, ComputedMemberAccessNode, ConditionalExpression, ConditionalNode, ContainerAllowedChildren, ContainerComponent, ContainerField, ContainerFieldTypes, ContainerLogicConfig, CrossFieldDetectionContext, CssWrapper, CustomCondition, CustomFnConfig, CustomFunction, CustomFunctionOptions, CustomFunctionScope, CustomValidator, CustomValidatorConfig, DebouncedResourceLogicFnConfig, DeclarativeHttpValidatorConfig, DerivationLogConfig, DerivationLogLevel, DerivationLogicConfig, DerivedFromDeferredOptions, DynamicFormActionRegistry, DynamicFormAddonActionPresetRegistry, DynamicFormAddonRegistry, DynamicFormAddonSlotRegistry, DynamicFormFieldRegistry, DynamicText, DynamicValue, EvaluationContext, EvaluationScope, ExtractField, FieldAddonSupport, FieldBoundAddonActionContext, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldScope, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTreeRecord, FieldTypeDefinition, FieldValueCondition, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, FunctionHttpValidatorConfig, GroupAllowedChildren, GroupComponent, GroupContext, GroupField, HiddenField, HiddenScalar, HiddenValue, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, IdentifierNode, InferFormValue, InitializationTrackingOptions, JavascriptCondition, LazyComponentLoader, LeafFieldTypes, LiteralNode, Logger, LogicConfig, LogicTrigger, MapperFn, MemberAccessNode, MemoizeOptions, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, ObjectLiteralNode, OrCondition, OrphanAddonActionContext, PageAllowedChildren, PageField, Prettify, ReadonlyFieldTree, RegisteredActionRef, RegisteredFieldTypes, RegisteredWrapperTypes, RenderReadyInput, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowComponent, RowField, RowWrapper, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, StateLogicType, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TemplateAddon, TextAddon, TextElementType, TextField, TextProps, Token, TokenContext, Trigger, UnaryOpNode, ValidationError, ValidationExecutionConfig, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WarningTracker, WithInputSignals, WrapperAutoAssociations, WrapperConfig, WrapperFieldInputs, WrapperTypeDefinition };
|