@ng-forge/dynamic-forms 0.9.0-next.12 → 0.9.0-next.13
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-BgCrTME1.mjs → ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs} +9 -9
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-VHaBk_1l.mjs → ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs} +6 -6
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-VHaBk_1l.mjs.map → ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-CGJiiKBY.mjs → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-CGJiiKBY.mjs.map → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-BOdw40ZF.mjs → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs} +5 -5
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-BOdw40ZF.mjs.map → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-D72mQxlX.mjs → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-D72mQxlX.mjs.map → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-BvTQwoLg.mjs → ng-forge-dynamic-forms-group-field.component-C9xUHrXp.mjs} +9 -9
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-C9xUHrXp.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +334 -27
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-BAPGCjUs.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BbgkzKuI.mjs} +1572 -186
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BbgkzKuI.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BwNIx3R1.mjs +114 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-DDzmhJNn.mjs.map → ng-forge-dynamic-forms-page-field.component-BwNIx3R1.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs +34 -0
- package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-BIvEUoia.mjs.map → ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-Bo4zNJXY.mjs → ng-forge-dynamic-forms-text-field.component-CpgtswdO.mjs} +5 -5
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-Bo4zNJXY.mjs.map → ng-forge-dynamic-forms-text-field.component-CpgtswdO.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +1 -1
- package/types/ng-forge-dynamic-forms-integration.d.ts +241 -37
- package/types/ng-forge-dynamic-forms.d.ts +1671 -531
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-BgCrTME1.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-BvTQwoLg.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BAPGCjUs.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-DDzmhJNn.mjs +0 -114
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BIvEUoia.mjs +0 -34
|
@@ -1,13 +1,155 @@
|
|
|
1
1
|
import * as rxjs from 'rxjs';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import * as _angular_core from '@angular/core';
|
|
4
|
-
import { Signal,
|
|
4
|
+
import { Signal, Type, ResourceRef, ViewContainerRef, InjectionToken, Injector, WritableSignal, EnvironmentInjector, Provider, EnvironmentProviders, TemplateRef, Resource, PipeTransform } from '@angular/core';
|
|
5
5
|
import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
|
|
6
6
|
import * as _angular_forms_signals from '@angular/forms/signals';
|
|
7
7
|
import { FieldContext, TreeValidationResult, ValidationError as ValidationError$1, FieldTree, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
|
|
8
8
|
import { FormSchema } from '@ng-forge/dynamic-forms/schema';
|
|
9
9
|
import * as _angular_forms from '@angular/forms';
|
|
10
10
|
|
|
11
|
+
type DynamicText = string | Observable<string> | Signal<string>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generic reactive value — a static value, an Angular Signal, or an RxJS Observable.
|
|
15
|
+
*
|
|
16
|
+
* Generalises {@link DynamicText} for arbitrary value types. Used by
|
|
17
|
+
* cross-cutting properties that need to react to form state without forcing
|
|
18
|
+
* every consumer to deal with reactivity (e.g., `hidden` / `disabled` on
|
|
19
|
+
* addons).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const a: DynamicValue<boolean> = true;
|
|
24
|
+
* const b: DynamicValue<boolean> = signal(false);
|
|
25
|
+
* const c: DynamicValue<boolean> = isValid$;
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
type DynamicValue<T> = T | Signal<T> | Observable<T>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Universal addon slot names supported by every adapter.
|
|
32
|
+
*
|
|
33
|
+
* Adapters render `prefix` to the leading edge of a field and `suffix` to
|
|
34
|
+
* the trailing edge (mirrored in RTL). Additional slots may be contributed
|
|
35
|
+
* via {@link DynamicFormAddonSlotRegistry} module augmentation.
|
|
36
|
+
*/
|
|
37
|
+
type CommonAddonSlot = 'prefix' | 'suffix';
|
|
38
|
+
/**
|
|
39
|
+
* Module-augmentable registry of adapter-specific addon slot names.
|
|
40
|
+
*
|
|
41
|
+
* Adapters that expose slots beyond the universal `prefix` / `suffix` (e.g.,
|
|
42
|
+
* an "error overlay" slot specific to one design system) declare them here.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
47
|
+
* interface DynamicFormAddonSlotRegistry {
|
|
48
|
+
* 'mat-error-overlay': true;
|
|
49
|
+
* }
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
interface DynamicFormAddonSlotRegistry {
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Union of every valid addon slot — universal slots plus any registered via
|
|
57
|
+
* {@link DynamicFormAddonSlotRegistry} module augmentation.
|
|
58
|
+
*/
|
|
59
|
+
type AddonSlot = CommonAddonSlot | keyof DynamicFormAddonSlotRegistry;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Base shape every addon kind extends.
|
|
63
|
+
*
|
|
64
|
+
* Public contract:
|
|
65
|
+
* - `slot` chooses where the addon renders (prefix / suffix / adapter-specific).
|
|
66
|
+
* - `hidden` / `disabled` are reactive — re-evaluated when their underlying
|
|
67
|
+
* signals or observables emit.
|
|
68
|
+
* - Multiple addons in the same slot render in array order, left-to-right
|
|
69
|
+
* within the slot (LTR; mirrored in RTL).
|
|
70
|
+
*
|
|
71
|
+
* Note: `meta` is intentionally absent — adapters declare addon-specific
|
|
72
|
+
* fields via module augmentation rather than an untyped escape hatch.
|
|
73
|
+
*/
|
|
74
|
+
interface BaseAddon<TSlot extends AddonSlot = AddonSlot> {
|
|
75
|
+
/** Slot to render this addon in. */
|
|
76
|
+
readonly slot: TSlot;
|
|
77
|
+
/** CSS class applied to the addon host element. */
|
|
78
|
+
readonly className?: string;
|
|
79
|
+
/** Reactive visibility — when `true`, the addon is removed from layout. */
|
|
80
|
+
readonly hidden?: DynamicValue<boolean>;
|
|
81
|
+
/** Reactive disabled state — primarily relevant for interactive kinds. */
|
|
82
|
+
readonly disabled?: DynamicValue<boolean>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Module-augmentable registry of addon kinds.
|
|
86
|
+
*
|
|
87
|
+
* Each key is the kind discriminant; each value is the addon's full shape
|
|
88
|
+
* (extending {@link BaseAddon}). Core ships `text`, `template`, and
|
|
89
|
+
* `component`; adapters add their own (e.g., `prime-icon`, `prime-button`).
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
94
|
+
* interface DynamicFormAddonRegistry {
|
|
95
|
+
* 'prime-icon': PrimeIconAddon;
|
|
96
|
+
* 'prime-button': PrimeButtonAddon;
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
interface DynamicFormAddonRegistry {
|
|
102
|
+
text: TextAddon;
|
|
103
|
+
template: TemplateAddon;
|
|
104
|
+
component: ComponentAddon;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Union of every registered addon shape.
|
|
108
|
+
*/
|
|
109
|
+
type AnyAddon = DynamicFormAddonRegistry[keyof DynamicFormAddonRegistry];
|
|
110
|
+
/**
|
|
111
|
+
* Renders text content inline.
|
|
112
|
+
*
|
|
113
|
+
* `text` is `DynamicText`, supporting plain strings, observables, and
|
|
114
|
+
* signals — so values like `'$'`, `'.com'`, or interpolated counters
|
|
115
|
+
* (`'42/100'`) all work uniformly.
|
|
116
|
+
*/
|
|
117
|
+
interface TextAddon extends BaseAddon {
|
|
118
|
+
readonly kind: 'text';
|
|
119
|
+
readonly text: DynamicText;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Renders a named `<ng-template>` projected into the host `<df-dynamic-form>`.
|
|
123
|
+
*
|
|
124
|
+
* JSON-safe — backends ship the key, FE supplies the template definition.
|
|
125
|
+
* Use the `templateAddon(ref)` helper for rename-safe authoring in code.
|
|
126
|
+
*/
|
|
127
|
+
interface TemplateAddon extends BaseAddon {
|
|
128
|
+
readonly kind: 'template';
|
|
129
|
+
readonly templateKey: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Renders an arbitrary Angular component as the addon body.
|
|
133
|
+
*
|
|
134
|
+
* The loader returns either a component class directly or a module with a
|
|
135
|
+
* `default` export. For non-default exports, unwrap with `.then(m => m.X)`:
|
|
136
|
+
*
|
|
137
|
+
* ```ts
|
|
138
|
+
* { kind: 'component', component: () => import('./my').then(m => m.MyComponent) }
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @codeOnly The `component` loader is a function and cannot survive
|
|
142
|
+
* `JSON.stringify`/`parse`. The lenient validator drops this kind when the
|
|
143
|
+
* config originated from JSON (set via `sanitizeFormConfig({ source: 'json' })`).
|
|
144
|
+
*/
|
|
145
|
+
interface ComponentAddon extends BaseAddon {
|
|
146
|
+
readonly kind: 'component';
|
|
147
|
+
readonly component: () => Promise<Type<unknown> | {
|
|
148
|
+
default: Type<unknown>;
|
|
149
|
+
}>;
|
|
150
|
+
readonly inputs?: Record<string, unknown>;
|
|
151
|
+
}
|
|
152
|
+
|
|
11
153
|
type WithInputSignals<T> = {
|
|
12
154
|
readonly [K in keyof T]-?: Signal<T[K]>;
|
|
13
155
|
};
|
|
@@ -2098,8 +2240,6 @@ interface SchemaDefinition {
|
|
|
2098
2240
|
subSchemas?: SchemaApplicationConfig[];
|
|
2099
2241
|
}
|
|
2100
2242
|
|
|
2101
|
-
type DynamicText = string | Observable<string> | Signal<string>;
|
|
2102
|
-
|
|
2103
2243
|
/**
|
|
2104
2244
|
* Re-export Angular's ValidationError for consistency
|
|
2105
2245
|
* This replaces our custom ValidationError interface
|
|
@@ -4195,6 +4335,24 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
|
|
|
4195
4335
|
* and any field-level `wrappers`.
|
|
4196
4336
|
*/
|
|
4197
4337
|
skipDefaultWrappers?: boolean;
|
|
4338
|
+
/**
|
|
4339
|
+
* Addons rendered inside this field's slots (typically `prefix` / `suffix`).
|
|
4340
|
+
*
|
|
4341
|
+
* Each addon declares its kind, slot, and kind-specific configuration.
|
|
4342
|
+
* Whether a field type accepts addons — and which slots / kinds — is
|
|
4343
|
+
* declared at registration via `FieldTypeDefinition.addons`. Field types
|
|
4344
|
+
* without that registration ("Tier 3", e.g., toggle, radio) drop any
|
|
4345
|
+
* `addons` value with a lenient warning at config init.
|
|
4346
|
+
*
|
|
4347
|
+
* @example
|
|
4348
|
+
* ```typescript
|
|
4349
|
+
* addons: [
|
|
4350
|
+
* { slot: 'prefix', kind: 'prime-icon', icon: 'search' },
|
|
4351
|
+
* { slot: 'suffix', kind: 'prime-button', preset: 'clear', ariaLabel: 'Clear' },
|
|
4352
|
+
* ]
|
|
4353
|
+
* ```
|
|
4354
|
+
*/
|
|
4355
|
+
addons?: ReadonlyArray<AnyAddon>;
|
|
4198
4356
|
}
|
|
4199
4357
|
type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
|
|
4200
4358
|
/**
|
|
@@ -4289,6 +4447,103 @@ interface GroupContext {
|
|
|
4289
4447
|
*/
|
|
4290
4448
|
type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
|
|
4291
4449
|
|
|
4450
|
+
/**
|
|
4451
|
+
* Minimal JSON Schema fragment used by the MCP server to surface an addon
|
|
4452
|
+
* kind's shape to AI form generators. Covers the common subset
|
|
4453
|
+
* (`type`/`properties`/`required`/`enum`/etc.) without pulling in a full JSON
|
|
4454
|
+
* Schema dependency. Extra keys are allowed via the `[key: string]` index so
|
|
4455
|
+
* vendor-specific annotations (`x-foo`, `markdownDescription`) still typecheck.
|
|
4456
|
+
*
|
|
4457
|
+
* Tooling-only: the runtime addon validator does NOT consult `schema` — it
|
|
4458
|
+
* relies on each kind's `validate?(addon, fieldKey)` callback for shape
|
|
4459
|
+
* enforcement. `schema` exists exclusively so the MCP server (and any other
|
|
4460
|
+
* AI-driven form authoring tool) can describe the kind to a model without
|
|
4461
|
+
* hand-maintaining a parallel registry.
|
|
4462
|
+
*/
|
|
4463
|
+
interface AddonKindSchema {
|
|
4464
|
+
readonly $ref?: string;
|
|
4465
|
+
readonly $schema?: string;
|
|
4466
|
+
readonly title?: string;
|
|
4467
|
+
readonly description?: string;
|
|
4468
|
+
readonly type?: 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null' | ReadonlyArray<'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'>;
|
|
4469
|
+
readonly enum?: ReadonlyArray<unknown>;
|
|
4470
|
+
readonly const?: unknown;
|
|
4471
|
+
readonly properties?: Readonly<Record<string, AddonKindSchema>>;
|
|
4472
|
+
readonly required?: ReadonlyArray<string>;
|
|
4473
|
+
readonly additionalProperties?: boolean | AddonKindSchema;
|
|
4474
|
+
readonly items?: AddonKindSchema | ReadonlyArray<AddonKindSchema>;
|
|
4475
|
+
readonly oneOf?: ReadonlyArray<AddonKindSchema>;
|
|
4476
|
+
readonly anyOf?: ReadonlyArray<AddonKindSchema>;
|
|
4477
|
+
readonly allOf?: ReadonlyArray<AddonKindSchema>;
|
|
4478
|
+
readonly not?: AddonKindSchema;
|
|
4479
|
+
readonly default?: unknown;
|
|
4480
|
+
readonly examples?: ReadonlyArray<unknown>;
|
|
4481
|
+
readonly minimum?: number;
|
|
4482
|
+
readonly maximum?: number;
|
|
4483
|
+
readonly minLength?: number;
|
|
4484
|
+
readonly maxLength?: number;
|
|
4485
|
+
readonly pattern?: string;
|
|
4486
|
+
readonly format?: string;
|
|
4487
|
+
/** Vendor extensions and unrecognised keywords pass through untyped. */
|
|
4488
|
+
readonly [key: string]: unknown;
|
|
4489
|
+
}
|
|
4490
|
+
/**
|
|
4491
|
+
* Optional shape validator a kind ships to enforce its required fields at
|
|
4492
|
+
* runtime. Throws are caught by the validator and converted into lenient
|
|
4493
|
+
* warnings; the offending addon is dropped and the form keeps rendering.
|
|
4494
|
+
*/
|
|
4495
|
+
type AddonShapeValidator<T extends BaseAddon = BaseAddon> = (addon: T, fieldKey: string) => void;
|
|
4496
|
+
/**
|
|
4497
|
+
* Per-kind registration metadata.
|
|
4498
|
+
*
|
|
4499
|
+
* Registered via `withCustomAddon(...)` features (or adapter feature
|
|
4500
|
+
* helpers like `withPrimeNGAddons()`); resolved at render time by
|
|
4501
|
+
* `<df-addon-slot>`.
|
|
4502
|
+
*/
|
|
4503
|
+
interface AddonKindDefinition<T extends BaseAddon = BaseAddon> {
|
|
4504
|
+
/** Discriminant matching `addon.kind`. */
|
|
4505
|
+
readonly kind: string;
|
|
4506
|
+
/** Lazy loader for the kind's renderer component. */
|
|
4507
|
+
readonly loadComponent: LazyComponentLoader;
|
|
4508
|
+
/** Optional shape validator — throw `DynamicFormError` to drop the addon with a warning. */
|
|
4509
|
+
readonly validate?: AddonShapeValidator<T>;
|
|
4510
|
+
/** Optional JSON Schema fragment for MCP / pre-flight tooling. */
|
|
4511
|
+
readonly schema?: AddonKindSchema;
|
|
4512
|
+
/**
|
|
4513
|
+
* Whether the kind survives `JSON.stringify` / `JSON.parse`. Kinds whose
|
|
4514
|
+
* shape includes a function payload (e.g., the built-in `'component'`
|
|
4515
|
+
* loader, inline `prime-button` `action` handlers) declare `jsonSafe: false`
|
|
4516
|
+
* so the validator drops them when the config was loaded from a JSON
|
|
4517
|
+
* source (`sanitizeFormConfig(config, { source: 'json' })`). Defaults to
|
|
4518
|
+
* `true` — kinds are JSON-safe unless explicitly opted out.
|
|
4519
|
+
*/
|
|
4520
|
+
readonly jsonSafe?: boolean;
|
|
4521
|
+
}
|
|
4522
|
+
/**
|
|
4523
|
+
* Per-field-type addon-support metadata declared at field registration.
|
|
4524
|
+
*
|
|
4525
|
+
* Source of truth for the runtime validator: which slots are valid for a
|
|
4526
|
+
* field type, which kinds are accepted (optional whitelist).
|
|
4527
|
+
*
|
|
4528
|
+
* Field types that do not declare `addons` on their `FieldTypeDefinition`
|
|
4529
|
+
* are treated as Tier 3 — addons configured on such fields are dropped
|
|
4530
|
+
* with a warning at config init.
|
|
4531
|
+
*/
|
|
4532
|
+
interface FieldAddonSupport {
|
|
4533
|
+
/** Slots a field of this type accepts. */
|
|
4534
|
+
readonly slots: readonly AddonSlot[];
|
|
4535
|
+
/** Optional whitelist of allowed addon kinds. Omitted = any registered kind. */
|
|
4536
|
+
readonly allowedKinds?: readonly string[];
|
|
4537
|
+
}
|
|
4538
|
+
/**
|
|
4539
|
+
* Global registry of addon kinds.
|
|
4540
|
+
*
|
|
4541
|
+
* Populated by `withCustomAddon(...)` features and adapter feature helpers
|
|
4542
|
+
* (e.g., `withPrimeNGAddons()`). Read by `<df-addon-slot>` to resolve a
|
|
4543
|
+
* kind discriminant to its renderer component.
|
|
4544
|
+
*/
|
|
4545
|
+
declare const ADDON_KIND_REGISTRY: InjectionToken<Map<string, AddonKindDefinition<BaseAddon<AddonSlot>>>>;
|
|
4546
|
+
|
|
4292
4547
|
/**
|
|
4293
4548
|
* Defines how a field type handles form values and data collection.
|
|
4294
4549
|
*
|
|
@@ -4382,6 +4637,23 @@ interface FieldTypeDefinition<T extends FieldDef<any> = any> {
|
|
|
4382
4637
|
scope?: FieldScope | FieldScope[];
|
|
4383
4638
|
/** Mapped component inputs that must exist before the renderer instantiates the component */
|
|
4384
4639
|
renderReadyWhen?: readonly RenderReadyInput[];
|
|
4640
|
+
/**
|
|
4641
|
+
* Declares which addon slots this field accepts and (optionally) the
|
|
4642
|
+
* subset of kinds allowed.
|
|
4643
|
+
*
|
|
4644
|
+
* Source of truth for the runtime addon validator. Field types that
|
|
4645
|
+
* omit this property are treated as Tier 3 — any `addons` configured
|
|
4646
|
+
* on such a field are dropped with a warning at config init.
|
|
4647
|
+
*
|
|
4648
|
+
* @example
|
|
4649
|
+
* ```typescript
|
|
4650
|
+
* {
|
|
4651
|
+
* name: 'prime-input',
|
|
4652
|
+
* addons: { slots: ['prefix', 'suffix'] },
|
|
4653
|
+
* }
|
|
4654
|
+
* ```
|
|
4655
|
+
*/
|
|
4656
|
+
addons?: FieldAddonSupport;
|
|
4385
4657
|
}
|
|
4386
4658
|
/**
|
|
4387
4659
|
* Injection token for the global field type registry.
|
|
@@ -4626,12 +4898,22 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
4626
4898
|
formOptions: _angular_core.InputSignal<FormOptions | undefined>;
|
|
4627
4899
|
/** Form values for two-way data binding. */
|
|
4628
4900
|
value: _angular_core.ModelSignal<Partial<TModel> | undefined>;
|
|
4901
|
+
/**
|
|
4902
|
+
* How lenient the addon validator is on `config.fields[...].addons`.
|
|
4903
|
+
* `'inline'` (default) keeps everything; `'json'` drops code-only kinds
|
|
4904
|
+
* with a warning. Read at form init and on every config swap — change
|
|
4905
|
+
* `source` AND the config to force re-validation mid-flight.
|
|
4906
|
+
*/
|
|
4907
|
+
source: _angular_core.InputSignal<"json" | "inline">;
|
|
4629
4908
|
private destroyRef;
|
|
4630
4909
|
private injector;
|
|
4631
4910
|
protected environmentInjector: EnvironmentInjector;
|
|
4632
4911
|
private eventBus;
|
|
4633
4912
|
private logger;
|
|
4634
4913
|
private dispatcher;
|
|
4914
|
+
private templateRegistry;
|
|
4915
|
+
/** All `<ng-template dfTemplate="...">` instances projected into this form. */
|
|
4916
|
+
private readonly _projectedTemplates;
|
|
4635
4917
|
/** State manager that owns all form state. Initialized via connectDeps() to guarantee
|
|
4636
4918
|
* FORM_STATE_DEPS is populated before FormStateManager is injected. */
|
|
4637
4919
|
private readonly stateManager;
|
|
@@ -4745,7 +5027,7 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
4745
5027
|
private connectDeps;
|
|
4746
5028
|
private setupEventHandlers;
|
|
4747
5029
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
|
|
4748
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicForm<any, any>, "form[dynamic-form]", never, { "config": { "alias": "dynamic-form"; "required": true; "isSignal": true; }; "formOptions": { "alias": "formOptions"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; "reset": "reset"; "cleared": "cleared"; "events": "events"; "initialized": "initialized"; "onPageChange": "onPageChange"; "onPageNavigationStateChange": "onPageNavigationStateChange"; },
|
|
5030
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicForm<any, any>, "form[dynamic-form]", never, { "config": { "alias": "dynamic-form"; "required": true; "isSignal": true; }; "formOptions": { "alias": "formOptions"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "source": { "alias": "source"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; "reset": "reset"; "cleared": "cleared"; "events": "events"; "initialized": "initialized"; "onPageChange": "onPageChange"; "onPageNavigationStateChange": "onPageNavigationStateChange"; }, ["_projectedTemplates"], never, true, never>;
|
|
4749
5031
|
}
|
|
4750
5032
|
|
|
4751
5033
|
declare class EventBus {
|
|
@@ -4920,178 +5202,99 @@ interface DynamicFormFeature<TKind extends string = string> {
|
|
|
4920
5202
|
ɵproviders: Provider[];
|
|
4921
5203
|
}
|
|
4922
5204
|
|
|
4923
|
-
interface FieldOption<T = unknown> {
|
|
4924
|
-
label: DynamicText;
|
|
4925
|
-
value: T;
|
|
4926
|
-
disabled?: boolean;
|
|
4927
|
-
[key: string]: unknown;
|
|
4928
|
-
}
|
|
4929
|
-
|
|
4930
5205
|
/**
|
|
4931
|
-
*
|
|
4932
|
-
*
|
|
4933
|
-
* Provides a Signal of the `defaultWrappers` array from FormConfig. Consumed by
|
|
4934
|
-
* `DfFieldOutlet` / `ContainerFieldComponent` to merge into each field's
|
|
4935
|
-
* effective wrapper chain (lowest priority after auto-associations, higher
|
|
4936
|
-
* than field-level `wrappers`).
|
|
5206
|
+
* User-facing wrapper registration shape used with `createWrappers(...)`.
|
|
4937
5207
|
*
|
|
4938
|
-
*
|
|
4939
|
-
*
|
|
5208
|
+
* Extends `WrapperTypeDefinition` with an optional `props` field that
|
|
5209
|
+
* carries the wrapper's config type (via `wrapperProps`) for later
|
|
5210
|
+
* inference by `InferWrapperRegistry`.
|
|
4940
5211
|
*/
|
|
4941
|
-
|
|
5212
|
+
interface WrapperRegistration<TName extends string = string, TConfig = unknown> {
|
|
5213
|
+
/** Unique identifier for the wrapper type */
|
|
5214
|
+
readonly wrapperName: TName;
|
|
5215
|
+
/** Lazy-loader for the wrapper component */
|
|
5216
|
+
readonly loadComponent: LazyComponentLoader;
|
|
5217
|
+
/** Field types this wrapper auto-applies to (merged lowest-priority during wrapper resolution) */
|
|
5218
|
+
readonly types?: readonly string[];
|
|
5219
|
+
/**
|
|
5220
|
+
* Type carrier only — runtime value is always `undefined`.
|
|
5221
|
+
* Use `wrapperProps<YourConfig>()` to thread the config type.
|
|
5222
|
+
*/
|
|
5223
|
+
readonly props?: TConfig;
|
|
5224
|
+
}
|
|
4942
5225
|
/**
|
|
4943
|
-
*
|
|
4944
|
-
*
|
|
4945
|
-
* The field signal context is the "nervous system" of the dynamic form library,
|
|
4946
|
-
* providing access to:
|
|
4947
|
-
* - The form instance and structure
|
|
4948
|
-
* - Current form values (as signals)
|
|
4949
|
-
* - Default values
|
|
4950
|
-
* - Validation messages
|
|
4951
|
-
* - The injector for creating components
|
|
5226
|
+
* Branded bundle returned by `createWrappers(...)`.
|
|
4952
5227
|
*
|
|
4953
|
-
*
|
|
4954
|
-
*
|
|
5228
|
+
* Carries:
|
|
5229
|
+
* - `ɵkind: 'wrappers'` — discriminant recognised by `provideDynamicForm(...)`
|
|
5230
|
+
* - `ɵregistrations` — the original registrations (type-level use for `InferWrapperRegistry`)
|
|
5231
|
+
* - `ɵdefinitions` — the `WrapperTypeDefinition[]` extracted for `WRAPPER_REGISTRY`
|
|
5232
|
+
*/
|
|
5233
|
+
interface WrappersBundle<T extends readonly WrapperRegistration[] = readonly WrapperRegistration[]> {
|
|
5234
|
+
readonly ɵkind: 'wrappers';
|
|
5235
|
+
readonly ɵregistrations: T;
|
|
5236
|
+
readonly ɵdefinitions: readonly WrapperTypeDefinition[];
|
|
5237
|
+
}
|
|
5238
|
+
/**
|
|
5239
|
+
* Bundle wrapper registrations into a single object that can be passed to
|
|
5240
|
+
* `provideDynamicForm(...)`.
|
|
4955
5241
|
*
|
|
4956
5242
|
* @example
|
|
4957
5243
|
* ```typescript
|
|
4958
|
-
*
|
|
4959
|
-
*
|
|
4960
|
-
*
|
|
4961
|
-
*
|
|
4962
|
-
*
|
|
4963
|
-
*
|
|
5244
|
+
* const appWrappers = createWrappers(
|
|
5245
|
+
* {
|
|
5246
|
+
* wrapperName: 'section',
|
|
5247
|
+
* loadComponent: () => import('./section-wrapper'),
|
|
5248
|
+
* props: wrapperProps<SectionWrapper>(),
|
|
5249
|
+
* },
|
|
5250
|
+
* {
|
|
5251
|
+
* wrapperName: 'highlight',
|
|
5252
|
+
* loadComponent: () => import('./highlight-wrapper'),
|
|
5253
|
+
* types: ['input', 'select'],
|
|
5254
|
+
* props: wrapperProps<HighlightWrapper>(),
|
|
5255
|
+
* },
|
|
5256
|
+
* );
|
|
4964
5257
|
*
|
|
4965
|
-
*
|
|
4966
|
-
*
|
|
4967
|
-
* private context = inject(FIELD_SIGNAL_CONTEXT);
|
|
5258
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
5259
|
+
* interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
|
|
4968
5260
|
* }
|
|
5261
|
+
*
|
|
5262
|
+
* bootstrapApplication(AppComponent, {
|
|
5263
|
+
* providers: [provideDynamicForm(appWrappers)],
|
|
5264
|
+
* });
|
|
4969
5265
|
* ```
|
|
4970
5266
|
*/
|
|
4971
|
-
declare const
|
|
5267
|
+
declare function createWrappers<const T extends readonly WrapperRegistration[]>(...registrations: T): WrappersBundle<T>;
|
|
5268
|
+
/** Type guard for a `WrappersBundle`. */
|
|
5269
|
+
declare function isWrappersBundle(value: unknown): value is WrappersBundle;
|
|
4972
5270
|
/**
|
|
4973
|
-
*
|
|
4974
|
-
*
|
|
4975
|
-
* This token is optionally provided by ArrayFieldComponent when creating injectors
|
|
4976
|
-
* for array items. It contains metadata about the array item's position and parent.
|
|
5271
|
+
* Derive the `FieldRegistryWrappers` augmentation shape from a wrapper bundle.
|
|
4977
5272
|
*
|
|
4978
|
-
*
|
|
5273
|
+
* Maps each registration's `wrapperName` to its `props` type (the config carried
|
|
5274
|
+
* by `wrapperProps<T>()`). If a registration has no `props`, falls back to the
|
|
5275
|
+
* minimal discriminant shape `{ readonly type: wrapperName }`.
|
|
4979
5276
|
*
|
|
4980
5277
|
* @example
|
|
4981
5278
|
* ```typescript
|
|
4982
|
-
*
|
|
4983
|
-
*
|
|
4984
|
-
* const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
|
|
4985
|
-
* if (arrayContext) {
|
|
4986
|
-
* // Use arrayContext.index, arrayContext.arrayKey, etc.
|
|
4987
|
-
* }
|
|
5279
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
5280
|
+
* interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
|
|
4988
5281
|
* }
|
|
4989
5282
|
* ```
|
|
4990
5283
|
*/
|
|
4991
|
-
|
|
5284
|
+
type InferWrapperRegistry<T> = T extends WrappersBundle<infer R> ? {
|
|
5285
|
+
[Reg in R[number] as Reg['wrapperName']]: Reg extends {
|
|
5286
|
+
props: infer P;
|
|
5287
|
+
} ? Exclude<P, undefined> : {
|
|
5288
|
+
readonly type: Reg['wrapperName'];
|
|
5289
|
+
};
|
|
5290
|
+
} : never;
|
|
5291
|
+
|
|
4992
5292
|
/**
|
|
4993
|
-
*
|
|
4994
|
-
*
|
|
4995
|
-
*
|
|
4996
|
-
* 1. `GroupFieldComponent` provides a context whose `groupPath()` is the
|
|
4997
|
-
* parent path extended by its own key (e.g. `'address'` or `'user.address'`).
|
|
4998
|
-
* 2. `createArrayItemInjector` provides a sentinel with an empty `groupPath`,
|
|
4999
|
-
* resetting the chain at array boundaries — descendants of an array item
|
|
5000
|
-
* that re-enter a group compose paths INSIDE the array item, matching the
|
|
5001
|
-
* property-derivation collector's keying.
|
|
5002
|
-
*
|
|
5003
|
-
* `mapFieldToInputs` reads this to scope DOM IDs and override-store keys so
|
|
5004
|
-
* the same leaf key can appear in different groups without colliding (#401).
|
|
5005
|
-
*
|
|
5006
|
-
* Inject with `{ optional: true }` because top-level fields with no group
|
|
5007
|
-
* ancestor (and no enclosing array) won't have the token provided. The token
|
|
5008
|
-
* IS present for fields inside an array even when no inner group exists — the
|
|
5009
|
-
* array-boundary sentinel makes injection succeed with an empty `groupPath`.
|
|
5293
|
+
* Type guard to check if a field is a container field (page, row, group, or array)
|
|
5294
|
+
* Container fields have a 'fields' property and don't contribute values directly.
|
|
5295
|
+
* This overload works with RegisteredFieldTypes for full type narrowing.
|
|
5010
5296
|
*/
|
|
5011
|
-
declare
|
|
5012
|
-
/**
|
|
5013
|
-
* Injection token for form-level default props.
|
|
5014
|
-
*
|
|
5015
|
-
* Default props are form-wide property defaults that are merged with field-level props.
|
|
5016
|
-
* Field props take precedence over default props.
|
|
5017
|
-
*
|
|
5018
|
-
* Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
|
|
5019
|
-
* DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
|
|
5020
|
-
* via Angular's hierarchical injector.
|
|
5021
|
-
*
|
|
5022
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5023
|
-
* that read the signal inside their computed() will automatically update.
|
|
5024
|
-
*
|
|
5025
|
-
* @example
|
|
5026
|
-
* ```typescript
|
|
5027
|
-
* // In a mapper function
|
|
5028
|
-
* const defaultPropsSignal = inject(DEFAULT_PROPS);
|
|
5029
|
-
*
|
|
5030
|
-
* return computed(() => {
|
|
5031
|
-
* const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
|
|
5032
|
-
* const baseInputs = buildBaseInputs(fieldDef, defaultProps);
|
|
5033
|
-
* // ...
|
|
5034
|
-
* });
|
|
5035
|
-
* ```
|
|
5036
|
-
*/
|
|
5037
|
-
declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
|
|
5038
|
-
/**
|
|
5039
|
-
* Injection token for form-level default validation messages.
|
|
5040
|
-
*
|
|
5041
|
-
* Default validation messages act as fallbacks when fields have validation
|
|
5042
|
-
* errors but no field-level `validationMessages` defined.
|
|
5043
|
-
*
|
|
5044
|
-
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5045
|
-
* and inherited by all children via Angular's hierarchical injector.
|
|
5046
|
-
*
|
|
5047
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5048
|
-
* that read the signal inside their computed() will automatically update.
|
|
5049
|
-
*
|
|
5050
|
-
* @example
|
|
5051
|
-
* ```typescript
|
|
5052
|
-
* // In a mapper or component
|
|
5053
|
-
* const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
|
|
5054
|
-
*
|
|
5055
|
-
* return computed(() => {
|
|
5056
|
-
* const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
|
|
5057
|
-
* const message = defaultMessages?.required ?? 'Field is required';
|
|
5058
|
-
* // ...
|
|
5059
|
-
* });
|
|
5060
|
-
* ```
|
|
5061
|
-
*/
|
|
5062
|
-
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
5063
|
-
/**
|
|
5064
|
-
* Injection token for form-level options.
|
|
5065
|
-
*
|
|
5066
|
-
* Form options control form-wide behavior including button disabled states.
|
|
5067
|
-
* Used by button mappers to determine default disabled behavior.
|
|
5068
|
-
*
|
|
5069
|
-
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5070
|
-
* and inherited by all children via Angular's hierarchical injector.
|
|
5071
|
-
*
|
|
5072
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5073
|
-
* that read the signal inside their computed() will automatically update.
|
|
5074
|
-
*
|
|
5075
|
-
* @example
|
|
5076
|
-
* ```typescript
|
|
5077
|
-
* // In a button mapper
|
|
5078
|
-
* const formOptionsSignal = inject(FORM_OPTIONS);
|
|
5079
|
-
*
|
|
5080
|
-
* return computed(() => {
|
|
5081
|
-
* const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
|
|
5082
|
-
* const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
|
|
5083
|
-
* // ...
|
|
5084
|
-
* });
|
|
5085
|
-
* ```
|
|
5086
|
-
*/
|
|
5087
|
-
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
5088
|
-
|
|
5089
|
-
/**
|
|
5090
|
-
* Type guard to check if a field is a container field (page, row, group, or array)
|
|
5091
|
-
* Container fields have a 'fields' property and don't contribute values directly.
|
|
5092
|
-
* This overload works with RegisteredFieldTypes for full type narrowing.
|
|
5093
|
-
*/
|
|
5094
|
-
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
5297
|
+
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
5095
5298
|
/**
|
|
5096
5299
|
* Type guard to check if a field is a container field (page, row, group, or array).
|
|
5097
5300
|
* This overload works with any FieldDef for looser type checking.
|
|
@@ -5128,93 +5331,6 @@ type FieldPathAccess<TValue> = {
|
|
|
5128
5331
|
[K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
|
|
5129
5332
|
};
|
|
5130
5333
|
|
|
5131
|
-
/**
|
|
5132
|
-
* User-facing wrapper registration shape used with `createWrappers(...)`.
|
|
5133
|
-
*
|
|
5134
|
-
* Extends `WrapperTypeDefinition` with an optional `props` field that
|
|
5135
|
-
* carries the wrapper's config type (via `wrapperProps`) for later
|
|
5136
|
-
* inference by `InferWrapperRegistry`.
|
|
5137
|
-
*/
|
|
5138
|
-
interface WrapperRegistration<TName extends string = string, TConfig = unknown> {
|
|
5139
|
-
/** Unique identifier for the wrapper type */
|
|
5140
|
-
readonly wrapperName: TName;
|
|
5141
|
-
/** Lazy-loader for the wrapper component */
|
|
5142
|
-
readonly loadComponent: LazyComponentLoader;
|
|
5143
|
-
/** Field types this wrapper auto-applies to (merged lowest-priority during wrapper resolution) */
|
|
5144
|
-
readonly types?: readonly string[];
|
|
5145
|
-
/**
|
|
5146
|
-
* Type carrier only — runtime value is always `undefined`.
|
|
5147
|
-
* Use `wrapperProps<YourConfig>()` to thread the config type.
|
|
5148
|
-
*/
|
|
5149
|
-
readonly props?: TConfig;
|
|
5150
|
-
}
|
|
5151
|
-
/**
|
|
5152
|
-
* Branded bundle returned by `createWrappers(...)`.
|
|
5153
|
-
*
|
|
5154
|
-
* Carries:
|
|
5155
|
-
* - `ɵkind: 'wrappers'` — discriminant recognised by `provideDynamicForm(...)`
|
|
5156
|
-
* - `ɵregistrations` — the original registrations (type-level use for `InferWrapperRegistry`)
|
|
5157
|
-
* - `ɵdefinitions` — the `WrapperTypeDefinition[]` extracted for `WRAPPER_REGISTRY`
|
|
5158
|
-
*/
|
|
5159
|
-
interface WrappersBundle<T extends readonly WrapperRegistration[] = readonly WrapperRegistration[]> {
|
|
5160
|
-
readonly ɵkind: 'wrappers';
|
|
5161
|
-
readonly ɵregistrations: T;
|
|
5162
|
-
readonly ɵdefinitions: readonly WrapperTypeDefinition[];
|
|
5163
|
-
}
|
|
5164
|
-
/**
|
|
5165
|
-
* Bundle wrapper registrations into a single object that can be passed to
|
|
5166
|
-
* `provideDynamicForm(...)`.
|
|
5167
|
-
*
|
|
5168
|
-
* @example
|
|
5169
|
-
* ```typescript
|
|
5170
|
-
* const appWrappers = createWrappers(
|
|
5171
|
-
* {
|
|
5172
|
-
* wrapperName: 'section',
|
|
5173
|
-
* loadComponent: () => import('./section-wrapper'),
|
|
5174
|
-
* props: wrapperProps<SectionWrapper>(),
|
|
5175
|
-
* },
|
|
5176
|
-
* {
|
|
5177
|
-
* wrapperName: 'highlight',
|
|
5178
|
-
* loadComponent: () => import('./highlight-wrapper'),
|
|
5179
|
-
* types: ['input', 'select'],
|
|
5180
|
-
* props: wrapperProps<HighlightWrapper>(),
|
|
5181
|
-
* },
|
|
5182
|
-
* );
|
|
5183
|
-
*
|
|
5184
|
-
* declare module '@ng-forge/dynamic-forms' {
|
|
5185
|
-
* interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
|
|
5186
|
-
* }
|
|
5187
|
-
*
|
|
5188
|
-
* bootstrapApplication(AppComponent, {
|
|
5189
|
-
* providers: [provideDynamicForm(appWrappers)],
|
|
5190
|
-
* });
|
|
5191
|
-
* ```
|
|
5192
|
-
*/
|
|
5193
|
-
declare function createWrappers<const T extends readonly WrapperRegistration[]>(...registrations: T): WrappersBundle<T>;
|
|
5194
|
-
/** Type guard for a `WrappersBundle`. */
|
|
5195
|
-
declare function isWrappersBundle(value: unknown): value is WrappersBundle;
|
|
5196
|
-
/**
|
|
5197
|
-
* Derive the `FieldRegistryWrappers` augmentation shape from a wrapper bundle.
|
|
5198
|
-
*
|
|
5199
|
-
* Maps each registration's `wrapperName` to its `props` type (the config carried
|
|
5200
|
-
* by `wrapperProps<T>()`). If a registration has no `props`, falls back to the
|
|
5201
|
-
* minimal discriminant shape `{ readonly type: wrapperName }`.
|
|
5202
|
-
*
|
|
5203
|
-
* @example
|
|
5204
|
-
* ```typescript
|
|
5205
|
-
* declare module '@ng-forge/dynamic-forms' {
|
|
5206
|
-
* interface FieldRegistryWrappers extends InferWrapperRegistry<typeof appWrappers> {}
|
|
5207
|
-
* }
|
|
5208
|
-
* ```
|
|
5209
|
-
*/
|
|
5210
|
-
type InferWrapperRegistry<T> = T extends WrappersBundle<infer R> ? {
|
|
5211
|
-
[Reg in R[number] as Reg['wrapperName']]: Reg extends {
|
|
5212
|
-
props: infer P;
|
|
5213
|
-
} ? Exclude<P, undefined> : {
|
|
5214
|
-
readonly type: Reg['wrapperName'];
|
|
5215
|
-
};
|
|
5216
|
-
} : never;
|
|
5217
|
-
|
|
5218
5334
|
/**
|
|
5219
5335
|
* Extract FieldDef type from FieldTypeDefinition
|
|
5220
5336
|
*/
|
|
@@ -5307,336 +5423,1425 @@ type ExtractFormValue<T> = T extends {
|
|
|
5307
5423
|
} ? V : never;
|
|
5308
5424
|
|
|
5309
5425
|
/**
|
|
5310
|
-
*
|
|
5426
|
+
* Type representing a FieldTree for an array.
|
|
5427
|
+
* Extends FieldTree to add numeric indexing for accessing item FieldTrees.
|
|
5428
|
+
*/
|
|
5429
|
+
type ArrayFieldTree<T> = FieldTree<T[]> & {
|
|
5430
|
+
readonly [index: number]: FieldTree<T> | undefined;
|
|
5431
|
+
};
|
|
5432
|
+
/**
|
|
5433
|
+
* Get the length of an array FieldTree.
|
|
5434
|
+
*/
|
|
5435
|
+
declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
|
|
5436
|
+
/**
|
|
5437
|
+
* Read-only view of a single field's observable state.
|
|
5311
5438
|
*
|
|
5312
|
-
*
|
|
5313
|
-
*
|
|
5314
|
-
*
|
|
5439
|
+
* Whitelisted read signals copied from Angular Signal Forms' `FieldState` so wrappers
|
|
5440
|
+
* can observe a field without being able to mutate it. New write-capable members
|
|
5441
|
+
* added in future Angular versions are excluded by default (the `Pick` list stays
|
|
5442
|
+
* the source of truth).
|
|
5443
|
+
*
|
|
5444
|
+
* `value` is narrowed from `WritableSignal<TValue>` to `Signal<TValue>` so consumers
|
|
5445
|
+
* cannot write through it.
|
|
5315
5446
|
*/
|
|
5316
|
-
|
|
5447
|
+
interface ReadonlyFieldTree<TValue = unknown> {
|
|
5448
|
+
readonly value: Signal<TValue>;
|
|
5449
|
+
readonly valid: Signal<boolean>;
|
|
5450
|
+
readonly invalid: Signal<boolean>;
|
|
5451
|
+
readonly touched: Signal<boolean>;
|
|
5452
|
+
readonly dirty: Signal<boolean>;
|
|
5453
|
+
readonly required: Signal<boolean>;
|
|
5454
|
+
readonly disabled: Signal<boolean>;
|
|
5455
|
+
readonly hidden: Signal<boolean>;
|
|
5456
|
+
readonly errors: Signal<readonly unknown[]>;
|
|
5457
|
+
}
|
|
5458
|
+
/**
|
|
5459
|
+
* Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
|
|
5460
|
+
* Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
|
|
5461
|
+
* so consumers only see the narrow surface and Angular's `WritableSignal` capability
|
|
5462
|
+
* on `value` is hidden.
|
|
5463
|
+
*/
|
|
5464
|
+
declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
|
|
5465
|
+
/**
|
|
5466
|
+
* Write a value into a FieldTree's underlying signal. Signal Forms stores
|
|
5467
|
+
* `FieldState.value` as a `WritableSignal`; this helper centralises the cast
|
|
5468
|
+
* and warns if that contract ever changes (vs a silent no-op).
|
|
5469
|
+
*/
|
|
5470
|
+
declare function writeToFieldValue<T>(value: Signal<T>, next: T, logger?: {
|
|
5471
|
+
warn: (msg: string) => void;
|
|
5472
|
+
}): boolean;
|
|
5317
5473
|
|
|
5318
5474
|
/**
|
|
5319
|
-
*
|
|
5320
|
-
*
|
|
5321
|
-
* @public
|
|
5475
|
+
* Fields every addon-action context carries, regardless of whether the
|
|
5476
|
+
* addon is bound to a host field or rendered orphaned.
|
|
5322
5477
|
*/
|
|
5323
|
-
interface
|
|
5478
|
+
interface AddonActionContextBase<TValue> {
|
|
5324
5479
|
/**
|
|
5325
|
-
*
|
|
5326
|
-
*
|
|
5327
|
-
* @
|
|
5480
|
+
* Identity of the host field this addon is attached to. Empty strings
|
|
5481
|
+
* indicate the addon is rendered outside an active field context (rare).
|
|
5482
|
+
* Use {@link isFieldBoundContext} or the `form !== null` check to
|
|
5483
|
+
* narrow before relying on field identity.
|
|
5328
5484
|
*/
|
|
5329
|
-
|
|
5485
|
+
readonly field: {
|
|
5486
|
+
readonly key: string;
|
|
5487
|
+
readonly type: string;
|
|
5488
|
+
};
|
|
5330
5489
|
/**
|
|
5331
|
-
*
|
|
5332
|
-
*
|
|
5333
|
-
*
|
|
5334
|
-
* - `'summary'`: Log cycle completion with counts
|
|
5335
|
-
* - `'verbose'`: Log individual derivation evaluations with details
|
|
5336
|
-
*
|
|
5337
|
-
* @default 'none'
|
|
5490
|
+
* Current value of the host field at the time the action fires.
|
|
5491
|
+
* `undefined` when the host field has no value yet (initial render before
|
|
5492
|
+
* defaultValue resolution) or when the addon is rendered outside a field.
|
|
5338
5493
|
*/
|
|
5339
|
-
|
|
5494
|
+
readonly value: TValue | undefined;
|
|
5340
5495
|
}
|
|
5341
5496
|
/**
|
|
5342
|
-
*
|
|
5497
|
+
* Field-bound variant — the addon is attached to a real field and has a
|
|
5498
|
+
* working value-writer. The common case for `pi-button` / `mat-button` /
|
|
5499
|
+
* `bs-button` / `ion-button` inside an adapter input field.
|
|
5343
5500
|
*
|
|
5344
|
-
*
|
|
5345
|
-
*
|
|
5501
|
+
* `setValue` is non-optional here; the optional chain (`ctx.setValue?.()`)
|
|
5502
|
+
* is unnecessary noise once you've narrowed via {@link isFieldBoundContext}.
|
|
5503
|
+
*/
|
|
5504
|
+
interface FieldBoundAddonActionContext<TValue = unknown> extends AddonActionContextBase<TValue> {
|
|
5505
|
+
/** Read-only view of the host field's tree — same view wrappers receive. */
|
|
5506
|
+
readonly form: ReadonlyFieldTree<TValue>;
|
|
5507
|
+
/** Push a new value into the host field. */
|
|
5508
|
+
readonly setValue: (next: TValue) => void;
|
|
5509
|
+
}
|
|
5510
|
+
/**
|
|
5511
|
+
* Orphan variant — the addon is rendered outside any field component (rare;
|
|
5512
|
+
* happens when consumers compose `<df-addon-slot>` directly without a host
|
|
5513
|
+
* field). No writer is reachable; reading `value` may also yield `undefined`.
|
|
5514
|
+
*/
|
|
5515
|
+
interface OrphanAddonActionContext<TValue = unknown> extends AddonActionContextBase<TValue> {
|
|
5516
|
+
readonly form: null;
|
|
5517
|
+
/** Always absent in the orphan variant — distinguishes the union members. */
|
|
5518
|
+
readonly setValue?: undefined;
|
|
5519
|
+
}
|
|
5520
|
+
/**
|
|
5521
|
+
* Context handed to inline addon actions and registered handler functions.
|
|
5522
|
+
*
|
|
5523
|
+
* Discriminated by `form`: field-bound contexts have a non-null `form` and a
|
|
5524
|
+
* required `setValue`; orphan contexts have `form: null` and no `setValue`.
|
|
5525
|
+
* Use {@link isFieldBoundContext} when the handler needs to write back.
|
|
5346
5526
|
*
|
|
5347
5527
|
* @example
|
|
5348
5528
|
* ```typescript
|
|
5349
|
-
*
|
|
5350
|
-
*
|
|
5351
|
-
*
|
|
5352
|
-
*
|
|
5353
|
-
* )
|
|
5529
|
+
* provideAddonActions({
|
|
5530
|
+
* submit: (ctx) => {
|
|
5531
|
+
* if (!isFieldBoundContext(ctx)) return;
|
|
5532
|
+
* // ctx.setValue is now `(next: TValue) => void` — no optional chain.
|
|
5533
|
+
* myService.send(ctx.field.key, ctx.value, ctx.setValue);
|
|
5534
|
+
* },
|
|
5535
|
+
* });
|
|
5536
|
+
* ```
|
|
5537
|
+
*/
|
|
5538
|
+
type AddonActionContext<TValue = unknown> = FieldBoundAddonActionContext<TValue> | OrphanAddonActionContext<TValue>;
|
|
5539
|
+
/** Type guard narrowing {@link AddonActionContext} to its field-bound variant. */
|
|
5540
|
+
declare function isFieldBoundContext<TValue>(ctx: AddonActionContext<TValue>): ctx is FieldBoundAddonActionContext<TValue>;
|
|
5541
|
+
/**
|
|
5542
|
+
* Built-in preset actions shared across adapters.
|
|
5354
5543
|
*
|
|
5355
|
-
*
|
|
5356
|
-
*
|
|
5357
|
-
* ...withMaterialFields(),
|
|
5358
|
-
* withLoggerConfig({ derivations: 'verbose' })
|
|
5359
|
-
* )
|
|
5544
|
+
* Adapter kinds that accept a `preset` field render the corresponding
|
|
5545
|
+
* behaviour:
|
|
5360
5546
|
*
|
|
5361
|
-
*
|
|
5362
|
-
*
|
|
5363
|
-
*
|
|
5364
|
-
*
|
|
5365
|
-
*
|
|
5547
|
+
* - `'clear'`: empties the field value.
|
|
5548
|
+
* - `'reset'`: restores the field's configured default value (resolved
|
|
5549
|
+
* from the form's `defaultValues` map at click time); falls back to
|
|
5550
|
+
* empty when no default is reachable.
|
|
5551
|
+
* - `'paste'`: reads from the system clipboard and writes the result.
|
|
5552
|
+
* - `'copy'`: writes the field's current value to the system clipboard.
|
|
5553
|
+
* - `'toggle-password-visibility'`: flips a host input's `type` between
|
|
5554
|
+
* `password` and `text` (requires the host field to provide a type
|
|
5555
|
+
* override token, e.g., `PRIME_INPUT_TYPE_OVERRIDE`).
|
|
5366
5556
|
*
|
|
5367
|
-
*
|
|
5368
|
-
*
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
*
|
|
5557
|
+
* Form submission is intentionally NOT exposed as a button-addon preset —
|
|
5558
|
+
* use the dedicated `'submit'` field type instead.
|
|
5559
|
+
*/
|
|
5560
|
+
type CommonAddonActionPreset = 'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility';
|
|
5561
|
+
/**
|
|
5562
|
+
* Module-augmentable registry of adapter-specific preset names.
|
|
5373
5563
|
*
|
|
5374
|
-
*
|
|
5375
|
-
*
|
|
5564
|
+
* Adapters add their canonical actions (e.g., Material's
|
|
5565
|
+
* `'mat-datepicker-open'`) without forking the `AddonActionPreset` union.
|
|
5376
5566
|
*
|
|
5377
|
-
* @
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5567
|
+
* @example
|
|
5568
|
+
* ```typescript
|
|
5569
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
5570
|
+
* interface DynamicFormAddonActionPresetRegistry {
|
|
5571
|
+
* 'mat-datepicker-open': true;
|
|
5572
|
+
* }
|
|
5573
|
+
* }
|
|
5574
|
+
* ```
|
|
5575
|
+
*/
|
|
5576
|
+
interface DynamicFormAddonActionPresetRegistry {
|
|
5577
|
+
}
|
|
5381
5578
|
/**
|
|
5382
|
-
*
|
|
5579
|
+
* Union of every preset name an addon may reference — universal presets
|
|
5580
|
+
* plus any contributed via {@link DynamicFormAddonActionPresetRegistry}.
|
|
5383
5581
|
*
|
|
5384
|
-
*
|
|
5385
|
-
*
|
|
5582
|
+
* The `(string & {})` escape hatch keeps autocomplete on the known +
|
|
5583
|
+
* augmented presets while accepting arbitrary strings for one-off adapter
|
|
5584
|
+
* presets that don't warrant a module-augmentation. The runtime preset
|
|
5585
|
+
* runner already handles unknown names gracefully via its `default:` arm
|
|
5586
|
+
* (logs a warning, no throw). Mirrors `FieldDef.type`'s
|
|
5587
|
+
* `RegisteredFieldTypes['type'] | (string & {})` shape.
|
|
5386
5588
|
*/
|
|
5387
|
-
|
|
5388
|
-
|
|
5589
|
+
type AddonActionPreset = CommonAddonActionPreset | keyof DynamicFormAddonActionPresetRegistry | (string & {});
|
|
5389
5590
|
/**
|
|
5390
|
-
*
|
|
5591
|
+
* Module-augmentable registry of user-defined named action handlers.
|
|
5592
|
+
*
|
|
5593
|
+
* Users register handlers once at bootstrap with `provideAddonActions({...})`
|
|
5594
|
+
* and reference them from JSON-driven configs via `actionRef: 'handlerName'`.
|
|
5595
|
+
*
|
|
5596
|
+
* @example
|
|
5597
|
+
* ```typescript
|
|
5598
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
5599
|
+
* interface DynamicFormActionRegistry {
|
|
5600
|
+
* openProfile: true;
|
|
5601
|
+
* runWorkflow: true;
|
|
5602
|
+
* }
|
|
5603
|
+
* }
|
|
5604
|
+
* ```
|
|
5391
5605
|
*/
|
|
5392
|
-
|
|
5393
|
-
private readonly prefix;
|
|
5394
|
-
debug(message: string, ...args: unknown[]): void;
|
|
5395
|
-
info(message: string, ...args: unknown[]): void;
|
|
5396
|
-
warn(message: string, ...args: unknown[]): void;
|
|
5397
|
-
error(message: string, ...args: unknown[]): void;
|
|
5606
|
+
interface DynamicFormActionRegistry {
|
|
5398
5607
|
}
|
|
5608
|
+
/**
|
|
5609
|
+
* Typed string handle to a registered action handler. The empty registry
|
|
5610
|
+
* resolves to `never`; augmenting `DynamicFormActionRegistry` populates the
|
|
5611
|
+
* union and unlocks autocomplete.
|
|
5612
|
+
*/
|
|
5613
|
+
type RegisteredActionRef = keyof DynamicFormActionRegistry;
|
|
5399
5614
|
|
|
5400
5615
|
/**
|
|
5401
|
-
*
|
|
5402
|
-
*
|
|
5616
|
+
* Map of named `TemplateRef`s collected by `<df-dynamic-form>` from
|
|
5617
|
+
* `<ng-template dfTemplate="...">` children projected into its content.
|
|
5618
|
+
* The directive selector is `dfTemplate` (see `DfTemplate`); the input
|
|
5619
|
+
* value is the name addons reference via `templateKey`.
|
|
5620
|
+
*
|
|
5621
|
+
* The `template` addon kind looks up its `templateKey` in this map at
|
|
5622
|
+
* render time. Exposed as a `Signal` so projections can be added or
|
|
5623
|
+
* removed reactively (e.g., when the host conditionally projects).
|
|
5624
|
+
*
|
|
5625
|
+
* Provided at the form-component level (not root) so multiple
|
|
5626
|
+
* `<df-dynamic-form>` instances on the same page have independent
|
|
5627
|
+
* template scopes.
|
|
5403
5628
|
*/
|
|
5404
|
-
declare
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5629
|
+
declare const DF_FIELD_TEMPLATES: InjectionToken<Signal<ReadonlyMap<string, TemplateRef<unknown>>>>;
|
|
5630
|
+
|
|
5631
|
+
interface FieldOption<T = unknown> {
|
|
5632
|
+
label: DynamicText;
|
|
5633
|
+
value: T;
|
|
5634
|
+
disabled?: boolean;
|
|
5635
|
+
[key: string]: unknown;
|
|
5409
5636
|
}
|
|
5410
5637
|
|
|
5411
5638
|
/**
|
|
5412
|
-
*
|
|
5639
|
+
* Injection token for form-level default wrappers.
|
|
5413
5640
|
*
|
|
5414
|
-
*
|
|
5415
|
-
*
|
|
5416
|
-
*
|
|
5641
|
+
* Provides a Signal of the `defaultWrappers` array from FormConfig. Consumed by
|
|
5642
|
+
* `DfFieldOutlet` / `ContainerFieldComponent` to merge into each field's
|
|
5643
|
+
* effective wrapper chain (lowest priority after auto-associations, higher
|
|
5644
|
+
* than field-level `wrappers`).
|
|
5417
5645
|
*
|
|
5418
|
-
*
|
|
5646
|
+
* Provided once at the DynamicForm level and inherited via Angular's
|
|
5647
|
+
* hierarchical injector.
|
|
5648
|
+
*/
|
|
5649
|
+
declare const DEFAULT_WRAPPERS: InjectionToken<Signal<readonly WrapperConfig[] | undefined>>;
|
|
5650
|
+
/**
|
|
5651
|
+
* Injection token for providing field signal context to mappers and components.
|
|
5652
|
+
*
|
|
5653
|
+
* The field signal context is the "nervous system" of the dynamic form library,
|
|
5654
|
+
* providing access to:
|
|
5655
|
+
* - The form instance and structure
|
|
5656
|
+
* - Current form values (as signals)
|
|
5657
|
+
* - Default values
|
|
5658
|
+
* - Validation messages
|
|
5659
|
+
* - The injector for creating components
|
|
5660
|
+
*
|
|
5661
|
+
* This token is provided by the DynamicForm component and can be scoped
|
|
5662
|
+
* for nested forms (Groups, Arrays) via child injectors.
|
|
5663
|
+
*
|
|
5664
|
+
* @example
|
|
5419
5665
|
* ```typescript
|
|
5420
|
-
*
|
|
5421
|
-
*
|
|
5422
|
-
*
|
|
5423
|
-
* )
|
|
5666
|
+
* // In a mapper function — prefer the helper for required access (descriptive error).
|
|
5667
|
+
* export function mapValueField(fieldDef: BaseValueField<any, any>): Binding[] {
|
|
5668
|
+
* const context = injectFieldSignalContext();
|
|
5669
|
+
* const form = context.form();
|
|
5670
|
+
* // ... use context
|
|
5671
|
+
* }
|
|
5672
|
+
*
|
|
5673
|
+
* // Optional access — token directly, returns null if not provided.
|
|
5674
|
+
* const ctx = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
|
|
5675
|
+
* if (ctx) {
|
|
5676
|
+
* // ...
|
|
5677
|
+
* }
|
|
5424
5678
|
* ```
|
|
5679
|
+
*/
|
|
5680
|
+
declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
|
|
5681
|
+
/**
|
|
5682
|
+
* Throwing accessor for {@link FIELD_SIGNAL_CONTEXT}.
|
|
5425
5683
|
*
|
|
5426
|
-
*
|
|
5684
|
+
* Use when the consumer requires the context — group/array containers and
|
|
5685
|
+
* mappers that depend on form structure. Throws a descriptive
|
|
5686
|
+
* `DynamicFormError` instead of Angular's generic `NullInjectorError` when
|
|
5687
|
+
* the token isn't provided.
|
|
5688
|
+
*
|
|
5689
|
+
* Consumers that may legitimately run outside a `DynamicForm` should keep
|
|
5690
|
+
* using `inject(FIELD_SIGNAL_CONTEXT, { optional: true })`.
|
|
5691
|
+
*/
|
|
5692
|
+
declare function injectFieldSignalContext<TModel extends Record<string, unknown> = Record<string, unknown>>(): FieldSignalContext<TModel>;
|
|
5693
|
+
/**
|
|
5694
|
+
* Injection token for providing array context metadata to mappers and components.
|
|
5695
|
+
*
|
|
5696
|
+
* This token is optionally provided by ArrayFieldComponent when creating injectors
|
|
5697
|
+
* for array items. It contains metadata about the array item's position and parent.
|
|
5698
|
+
*
|
|
5699
|
+
* Mappers can inject this token with {optional: true} to access array context:
|
|
5700
|
+
*
|
|
5701
|
+
* @example
|
|
5427
5702
|
* ```typescript
|
|
5428
|
-
*
|
|
5429
|
-
*
|
|
5430
|
-
*
|
|
5703
|
+
* // In a mapper function
|
|
5704
|
+
* export function buttonMapper(fieldDef: FieldDef<any>): Binding[] {
|
|
5705
|
+
* const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
|
|
5706
|
+
* if (arrayContext) {
|
|
5707
|
+
* // Use arrayContext.index, arrayContext.arrayKey, etc.
|
|
5431
5708
|
* }
|
|
5709
|
+
* }
|
|
5710
|
+
* ```
|
|
5711
|
+
*/
|
|
5712
|
+
declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
|
|
5713
|
+
/**
|
|
5714
|
+
* Injection token for providing group ancestry to mappers and components.
|
|
5715
|
+
*
|
|
5716
|
+
* Re-provided in two places:
|
|
5717
|
+
* 1. `GroupFieldComponent` provides a context whose `groupPath()` is the
|
|
5718
|
+
* parent path extended by its own key (e.g. `'address'` or `'user.address'`).
|
|
5719
|
+
* 2. `createArrayItemInjector` provides a sentinel with an empty `groupPath`,
|
|
5720
|
+
* resetting the chain at array boundaries — descendants of an array item
|
|
5721
|
+
* that re-enter a group compose paths INSIDE the array item, matching the
|
|
5722
|
+
* property-derivation collector's keying.
|
|
5723
|
+
*
|
|
5724
|
+
* `mapFieldToInputs` reads this to scope DOM IDs and override-store keys so
|
|
5725
|
+
* the same leaf key can appear in different groups without colliding (#401).
|
|
5726
|
+
*
|
|
5727
|
+
* Inject with `{ optional: true }` because top-level fields with no group
|
|
5728
|
+
* ancestor (and no enclosing array) won't have the token provided. The token
|
|
5729
|
+
* IS present for fields inside an array even when no inner group exists — the
|
|
5730
|
+
* array-boundary sentinel makes injection succeed with an empty `groupPath`.
|
|
5731
|
+
*/
|
|
5732
|
+
declare const GROUP_CONTEXT: InjectionToken<GroupContext>;
|
|
5733
|
+
/**
|
|
5734
|
+
* Injection token for form-level default props.
|
|
5735
|
+
*
|
|
5736
|
+
* Default props are form-wide property defaults that are merged with field-level props.
|
|
5737
|
+
* Field props take precedence over default props.
|
|
5738
|
+
*
|
|
5739
|
+
* Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
|
|
5740
|
+
* DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
|
|
5741
|
+
* via Angular's hierarchical injector.
|
|
5742
|
+
*
|
|
5743
|
+
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5744
|
+
* that read the signal inside their computed() will automatically update.
|
|
5745
|
+
*
|
|
5746
|
+
* @example
|
|
5747
|
+
* ```typescript
|
|
5748
|
+
* // In a mapper function
|
|
5749
|
+
* const defaultPropsSignal = inject(DEFAULT_PROPS);
|
|
5750
|
+
*
|
|
5751
|
+
* return computed(() => {
|
|
5752
|
+
* const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
|
|
5753
|
+
* const baseInputs = buildBaseInputs(fieldDef, defaultProps);
|
|
5754
|
+
* // ...
|
|
5432
5755
|
* });
|
|
5433
5756
|
* ```
|
|
5757
|
+
*/
|
|
5758
|
+
declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
|
|
5759
|
+
/**
|
|
5760
|
+
* Injection token for form-level default validation messages.
|
|
5434
5761
|
*
|
|
5435
|
-
*
|
|
5762
|
+
* Default validation messages act as fallbacks when fields have validation
|
|
5763
|
+
* errors but no field-level `validationMessages` defined.
|
|
5764
|
+
*
|
|
5765
|
+
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5766
|
+
* and inherited by all children via Angular's hierarchical injector.
|
|
5767
|
+
*
|
|
5768
|
+
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5769
|
+
* that read the signal inside their computed() will automatically update.
|
|
5770
|
+
*
|
|
5771
|
+
* @example
|
|
5436
5772
|
* ```typescript
|
|
5437
|
-
*
|
|
5438
|
-
*
|
|
5439
|
-
*
|
|
5440
|
-
*
|
|
5773
|
+
* // In a mapper or component
|
|
5774
|
+
* const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
|
|
5775
|
+
*
|
|
5776
|
+
* return computed(() => {
|
|
5777
|
+
* const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
|
|
5778
|
+
* const message = defaultMessages?.required ?? 'Field is required';
|
|
5779
|
+
* // ...
|
|
5780
|
+
* });
|
|
5441
5781
|
* ```
|
|
5782
|
+
*/
|
|
5783
|
+
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
5784
|
+
/**
|
|
5785
|
+
* Injection token for form-level options.
|
|
5442
5786
|
*
|
|
5443
|
-
*
|
|
5444
|
-
*
|
|
5445
|
-
* 1. Per-form `false` - Disables for this form, regardless of global setting
|
|
5446
|
-
* 2. Per-form `true` - Enables for this form, regardless of global setting
|
|
5447
|
-
* 3. Per-form `undefined` - Uses global setting
|
|
5448
|
-
* 4. Global `withEventFormValue()` - Enables globally
|
|
5449
|
-
* 5. No global feature - Disabled globally (default)
|
|
5787
|
+
* Form options control form-wide behavior including button disabled states.
|
|
5788
|
+
* Used by button mappers to determine default disabled behavior.
|
|
5450
5789
|
*
|
|
5451
|
-
*
|
|
5790
|
+
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5791
|
+
* and inherited by all children via Angular's hierarchical injector.
|
|
5452
5792
|
*
|
|
5453
|
-
*
|
|
5793
|
+
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5794
|
+
* that read the signal inside their computed() will automatically update.
|
|
5795
|
+
*
|
|
5796
|
+
* @example
|
|
5797
|
+
* ```typescript
|
|
5798
|
+
* // In a button mapper
|
|
5799
|
+
* const formOptionsSignal = inject(FORM_OPTIONS);
|
|
5800
|
+
*
|
|
5801
|
+
* return computed(() => {
|
|
5802
|
+
* const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
|
|
5803
|
+
* const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
|
|
5804
|
+
* // ...
|
|
5805
|
+
* });
|
|
5806
|
+
* ```
|
|
5454
5807
|
*/
|
|
5455
|
-
declare
|
|
5808
|
+
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
5456
5809
|
|
|
5457
5810
|
/**
|
|
5458
|
-
*
|
|
5459
|
-
* based on their reactive state (hidden, disabled, readonly).
|
|
5811
|
+
* Built-in field types provided by the dynamic form library.
|
|
5460
5812
|
*
|
|
5461
|
-
*
|
|
5462
|
-
*
|
|
5813
|
+
* Each field type is validated at compile time using satisfies, ensuring
|
|
5814
|
+
* type safety of the mapper function while allowing the array to be typed
|
|
5815
|
+
* as FieldTypeDefinition[] for consumer flexibility.
|
|
5816
|
+
*/
|
|
5817
|
+
declare const BUILT_IN_FIELDS: FieldTypeDefinition[];
|
|
5818
|
+
|
|
5819
|
+
/**
|
|
5820
|
+
* Configuration options for the logger feature.
|
|
5463
5821
|
*
|
|
5464
5822
|
* @public
|
|
5465
5823
|
*/
|
|
5466
|
-
interface
|
|
5824
|
+
interface LoggerConfigOptions {
|
|
5467
5825
|
/**
|
|
5468
|
-
* Whether
|
|
5469
|
-
*
|
|
5470
|
-
* When `true`, fields whose `hidden()` state is `true` will have their
|
|
5471
|
-
* values omitted from the submitted form value.
|
|
5826
|
+
* Whether general logging is enabled.
|
|
5472
5827
|
*
|
|
5473
|
-
* @
|
|
5474
|
-
* This does NOT affect HiddenField (`type: 'hidden'`) — those fields
|
|
5475
|
-
* store values without UI and their `hidden()` state is always `false`.
|
|
5828
|
+
* @default true
|
|
5476
5829
|
*/
|
|
5477
|
-
|
|
5830
|
+
enabled?: boolean;
|
|
5478
5831
|
/**
|
|
5479
|
-
*
|
|
5832
|
+
* Derivation logging level.
|
|
5480
5833
|
*
|
|
5481
|
-
*
|
|
5482
|
-
*
|
|
5483
|
-
|
|
5484
|
-
readonly excludeValueIfDisabled?: boolean;
|
|
5485
|
-
/**
|
|
5486
|
-
* Whether to exclude the value of readonly fields from submission output.
|
|
5834
|
+
* - `'none'`: No derivation debug logging (default)
|
|
5835
|
+
* - `'summary'`: Log cycle completion with counts
|
|
5836
|
+
* - `'verbose'`: Log individual derivation evaluations with details
|
|
5487
5837
|
*
|
|
5488
|
-
*
|
|
5489
|
-
|
|
5838
|
+
* @default 'none'
|
|
5839
|
+
*/
|
|
5840
|
+
derivations?: DerivationLogLevel;
|
|
5841
|
+
}
|
|
5842
|
+
/**
|
|
5843
|
+
* Configure logging for dynamic forms.
|
|
5844
|
+
*
|
|
5845
|
+
* By default, general logging is enabled (ConsoleLogger) and derivation
|
|
5846
|
+
* logging is disabled ('none'). Use this feature to enable derivation debugging.
|
|
5847
|
+
*
|
|
5848
|
+
* @example
|
|
5849
|
+
* ```typescript
|
|
5850
|
+
* // Disable all logging
|
|
5851
|
+
* provideDynamicForm(
|
|
5852
|
+
* ...withMaterialFields(),
|
|
5853
|
+
* withLoggerConfig(false)
|
|
5854
|
+
* )
|
|
5855
|
+
*
|
|
5856
|
+
* // Enable verbose derivation logging
|
|
5857
|
+
* provideDynamicForm(
|
|
5858
|
+
* ...withMaterialFields(),
|
|
5859
|
+
* withLoggerConfig({ derivations: 'verbose' })
|
|
5860
|
+
* )
|
|
5861
|
+
*
|
|
5862
|
+
* // Disable derivation logging but keep general logging
|
|
5863
|
+
* provideDynamicForm(
|
|
5864
|
+
* ...withMaterialFields(),
|
|
5865
|
+
* withLoggerConfig({ derivations: 'none' })
|
|
5866
|
+
* )
|
|
5867
|
+
*
|
|
5868
|
+
* // Conditional logging (e.g., only in dev mode)
|
|
5869
|
+
* provideDynamicForm(
|
|
5870
|
+
* ...withMaterialFields(),
|
|
5871
|
+
* withLoggerConfig(() => isDevMode())
|
|
5872
|
+
* )
|
|
5873
|
+
* ```
|
|
5874
|
+
*
|
|
5875
|
+
* @param config - Boolean to enable/disable logging, a function returning boolean, or a config object
|
|
5876
|
+
* @returns A DynamicFormFeature that configures logging
|
|
5877
|
+
*
|
|
5878
|
+
* @public
|
|
5879
|
+
*/
|
|
5880
|
+
declare function withLoggerConfig(config?: boolean | (() => boolean) | LoggerConfigOptions): DynamicFormFeature<'logger'>;
|
|
5881
|
+
|
|
5882
|
+
/**
|
|
5883
|
+
* Injection token for the dynamic forms logger.
|
|
5884
|
+
*
|
|
5885
|
+
* Provided by provideDynamicForm() with ConsoleLogger as default.
|
|
5886
|
+
* Override with withLoggerConfig(false) to disable logging.
|
|
5887
|
+
*/
|
|
5888
|
+
declare const DynamicFormLogger: InjectionToken<Logger>;
|
|
5889
|
+
|
|
5890
|
+
/**
|
|
5891
|
+
* Console-based logger implementation.
|
|
5892
|
+
*/
|
|
5893
|
+
declare class ConsoleLogger implements Logger {
|
|
5894
|
+
private readonly prefix;
|
|
5895
|
+
debug(message: string, ...args: unknown[]): void;
|
|
5896
|
+
info(message: string, ...args: unknown[]): void;
|
|
5897
|
+
warn(message: string, ...args: unknown[]): void;
|
|
5898
|
+
error(message: string, ...args: unknown[]): void;
|
|
5899
|
+
}
|
|
5900
|
+
|
|
5901
|
+
/**
|
|
5902
|
+
* No-operation logger implementation.
|
|
5903
|
+
* All methods are no-ops - used in production by default.
|
|
5904
|
+
*/
|
|
5905
|
+
declare class NoopLogger implements Logger {
|
|
5906
|
+
debug(): void;
|
|
5907
|
+
info(): void;
|
|
5908
|
+
warn(): void;
|
|
5909
|
+
error(): void;
|
|
5910
|
+
}
|
|
5911
|
+
|
|
5912
|
+
/**
|
|
5913
|
+
* Enables automatic form value attachment to all events dispatched through the EventBus.
|
|
5914
|
+
*
|
|
5915
|
+
* When this feature is enabled, events dispatched via `eventBus.dispatch()` will include
|
|
5916
|
+
* the current form value in the `formValue` property. This is useful for event handlers
|
|
5917
|
+
* that need access to the complete form state at the time of the event.
|
|
5918
|
+
*
|
|
5919
|
+
* @example Global opt-in
|
|
5920
|
+
* ```typescript
|
|
5921
|
+
* provideDynamicForm(
|
|
5922
|
+
* ...withMaterialFields(),
|
|
5923
|
+
* withEventFormValue()
|
|
5924
|
+
* )
|
|
5925
|
+
* ```
|
|
5926
|
+
*
|
|
5927
|
+
* @example Consumer usage
|
|
5928
|
+
* ```typescript
|
|
5929
|
+
* eventBus.on<SubmitEvent>('submit').subscribe(event => {
|
|
5930
|
+
* if (hasFormValue(event)) {
|
|
5931
|
+
* console.log('Form value:', event.formValue);
|
|
5932
|
+
* }
|
|
5933
|
+
* });
|
|
5934
|
+
* ```
|
|
5935
|
+
*
|
|
5936
|
+
* @example Per-form disable (when globally enabled)
|
|
5937
|
+
* ```typescript
|
|
5938
|
+
* const config: FormConfig = {
|
|
5939
|
+
* fields: [...],
|
|
5940
|
+
* options: { emitFormValueOnEvents: false }
|
|
5941
|
+
* };
|
|
5942
|
+
* ```
|
|
5943
|
+
*
|
|
5944
|
+
* @remarks
|
|
5945
|
+
* **Precedence rules:**
|
|
5946
|
+
* 1. Per-form `false` - Disables for this form, regardless of global setting
|
|
5947
|
+
* 2. Per-form `true` - Enables for this form, regardless of global setting
|
|
5948
|
+
* 3. Per-form `undefined` - Uses global setting
|
|
5949
|
+
* 4. Global `withEventFormValue()` - Enables globally
|
|
5950
|
+
* 5. No global feature - Disabled globally (default)
|
|
5951
|
+
*
|
|
5952
|
+
* @returns A DynamicFormFeature that enables form value attachment to events
|
|
5953
|
+
*
|
|
5954
|
+
* @public
|
|
5955
|
+
*/
|
|
5956
|
+
declare function withEventFormValue(): DynamicFormFeature<'event-form-value'>;
|
|
5957
|
+
|
|
5958
|
+
/**
|
|
5959
|
+
* Configuration for excluding field values from form submission output
|
|
5960
|
+
* based on their reactive state (hidden, disabled, readonly).
|
|
5961
|
+
*
|
|
5962
|
+
* Supports a 3-tier configuration hierarchy: Field > Form > Global.
|
|
5963
|
+
* The most specific level wins for each property.
|
|
5964
|
+
*
|
|
5965
|
+
* @public
|
|
5966
|
+
*/
|
|
5967
|
+
interface ValueExclusionConfig {
|
|
5968
|
+
/**
|
|
5969
|
+
* Whether to exclude the value of hidden fields from submission output.
|
|
5970
|
+
*
|
|
5971
|
+
* When `true`, fields whose `hidden()` state is `true` will have their
|
|
5972
|
+
* values omitted from the submitted form value.
|
|
5973
|
+
*
|
|
5974
|
+
* @remarks
|
|
5975
|
+
* This does NOT affect HiddenField (`type: 'hidden'`) — those fields
|
|
5976
|
+
* store values without UI and their `hidden()` state is always `false`.
|
|
5977
|
+
*/
|
|
5978
|
+
readonly excludeValueIfHidden?: boolean;
|
|
5979
|
+
/**
|
|
5980
|
+
* Whether to exclude the value of disabled fields from submission output.
|
|
5981
|
+
*
|
|
5982
|
+
* When `true`, fields whose `disabled()` state is `true` will have their
|
|
5983
|
+
* values omitted from the submitted form value.
|
|
5984
|
+
*/
|
|
5985
|
+
readonly excludeValueIfDisabled?: boolean;
|
|
5986
|
+
/**
|
|
5987
|
+
* Whether to exclude the value of readonly fields from submission output.
|
|
5988
|
+
*
|
|
5989
|
+
* When `true`, fields whose `readonly()` state is `true` will have their
|
|
5990
|
+
* values omitted from the submitted form value.
|
|
5490
5991
|
*/
|
|
5491
5992
|
readonly excludeValueIfReadonly?: boolean;
|
|
5492
5993
|
}
|
|
5493
5994
|
/**
|
|
5494
|
-
* Fully resolved exclusion config with all properties required.
|
|
5495
|
-
* Produced by the 3-tier resolution: `field ?? form ?? global`.
|
|
5995
|
+
* Fully resolved exclusion config with all properties required.
|
|
5996
|
+
* Produced by the 3-tier resolution: `field ?? form ?? global`.
|
|
5997
|
+
*
|
|
5998
|
+
* @public
|
|
5999
|
+
*/
|
|
6000
|
+
type ResolvedValueExclusionConfig = Required<ValueExclusionConfig>;
|
|
6001
|
+
|
|
6002
|
+
/**
|
|
6003
|
+
* Configures global value exclusion defaults for form submission output.
|
|
6004
|
+
*
|
|
6005
|
+
* Value exclusion is **enabled by default** — field values are excluded from the
|
|
6006
|
+
* `(submitted)` output based on their reactive state. Use this feature to
|
|
6007
|
+
* override those defaults. This does NOT affect two-way binding (`value` model /
|
|
6008
|
+
* `entity`) — fields retain their values internally.
|
|
6009
|
+
*
|
|
6010
|
+
* @example Default behavior (all exclusions enabled)
|
|
6011
|
+
* ```typescript
|
|
6012
|
+
* provideDynamicForm(
|
|
6013
|
+
* ...withMaterialFields(),
|
|
6014
|
+
* withValueExclusionDefaults()
|
|
6015
|
+
* )
|
|
6016
|
+
* ```
|
|
6017
|
+
*
|
|
6018
|
+
* @example Disable specific exclusions
|
|
6019
|
+
* ```typescript
|
|
6020
|
+
* provideDynamicForm(
|
|
6021
|
+
* ...withMaterialFields(),
|
|
6022
|
+
* withValueExclusionDefaults({ excludeValueIfReadonly: false })
|
|
6023
|
+
* )
|
|
6024
|
+
* ```
|
|
6025
|
+
*
|
|
6026
|
+
* @example Disable all exclusions (restore pre-v1 behavior)
|
|
6027
|
+
* ```typescript
|
|
6028
|
+
* provideDynamicForm(
|
|
6029
|
+
* ...withMaterialFields(),
|
|
6030
|
+
* withValueExclusionDefaults({
|
|
6031
|
+
* excludeValueIfHidden: false,
|
|
6032
|
+
* excludeValueIfDisabled: false,
|
|
6033
|
+
* excludeValueIfReadonly: false,
|
|
6034
|
+
* })
|
|
6035
|
+
* )
|
|
6036
|
+
* ```
|
|
6037
|
+
*
|
|
6038
|
+
* @remarks
|
|
6039
|
+
* **Precedence rules:**
|
|
6040
|
+
* 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
|
|
6041
|
+
* 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
|
|
6042
|
+
* 3. Global `withValueExclusionDefaults()` — baseline default
|
|
6043
|
+
* 4. No global feature — uses token default (all enabled)
|
|
6044
|
+
*
|
|
6045
|
+
* @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
|
|
6046
|
+
* @returns A DynamicFormFeature that configures value exclusion defaults
|
|
6047
|
+
*
|
|
6048
|
+
* @public
|
|
6049
|
+
*/
|
|
6050
|
+
declare function withValueExclusionDefaults(config?: Partial<ValueExclusionConfig>): DynamicFormFeature<'value-exclusion'>;
|
|
6051
|
+
|
|
6052
|
+
/**
|
|
6053
|
+
* Configuration for when validation runs based on a field's reactive state.
|
|
6054
|
+
*
|
|
6055
|
+
* Currently controls whether hidden fields are validated. Resolved through a
|
|
6056
|
+
* tree-aware hierarchy: per-field overrides inherit down to descendants, with
|
|
6057
|
+
* per-form `FormOptions` and global `withValidationExecutionDefaults()` providing
|
|
6058
|
+
* the root inherited value.
|
|
6059
|
+
*
|
|
6060
|
+
* @public
|
|
6061
|
+
*/
|
|
6062
|
+
interface ValidationExecutionConfig {
|
|
6063
|
+
/**
|
|
6064
|
+
* Whether validators run while a field is hidden (statically, via `hidden`
|
|
6065
|
+
* logic, or because any ancestor is hidden).
|
|
6066
|
+
*
|
|
6067
|
+
* When `false`, all validators on a hidden field are skipped — the field is
|
|
6068
|
+
* effectively inert until shown. State logic (hidden/disabled/readonly) and
|
|
6069
|
+
* value derivations continue to apply.
|
|
6070
|
+
*/
|
|
6071
|
+
readonly validateWhenHidden?: boolean;
|
|
6072
|
+
}
|
|
6073
|
+
/**
|
|
6074
|
+
* Fully resolved validation execution config — every property defined.
|
|
6075
|
+
*
|
|
6076
|
+
* @public
|
|
6077
|
+
*/
|
|
6078
|
+
type ResolvedValidationExecutionConfig = Required<ValidationExecutionConfig>;
|
|
6079
|
+
|
|
6080
|
+
/**
|
|
6081
|
+
* Configures global validation execution defaults — when validators should run
|
|
6082
|
+
* relative to a field's reactive state.
|
|
6083
|
+
*
|
|
6084
|
+
* By default, validators are **skipped** for hidden fields (`validateWhenHidden: false`),
|
|
6085
|
+
* matching what most users expect: a hidden field is inert. Use this feature to
|
|
6086
|
+
* restore the legacy behavior where hidden fields still validate.
|
|
6087
|
+
*
|
|
6088
|
+
* @example Default behavior — no feature needed
|
|
6089
|
+
* ```typescript
|
|
6090
|
+
* provideDynamicForm(...withMaterialFields())
|
|
6091
|
+
* // hidden fields skip validation
|
|
6092
|
+
* ```
|
|
6093
|
+
*
|
|
6094
|
+
* @example Restore legacy validation-on-hidden behavior
|
|
6095
|
+
* ```typescript
|
|
6096
|
+
* provideDynamicForm(
|
|
6097
|
+
* ...withMaterialFields(),
|
|
6098
|
+
* withValidationExecutionDefaults({ validateWhenHidden: true })
|
|
6099
|
+
* )
|
|
6100
|
+
* ```
|
|
6101
|
+
*
|
|
6102
|
+
* @remarks
|
|
6103
|
+
* **Precedence rules:**
|
|
6104
|
+
* 1. Per-field `FieldDef.validateWhenHidden` — wins for that field; inherits down to descendants
|
|
6105
|
+
* that don't override.
|
|
6106
|
+
* 2. Per-form `FormOptions.validateWhenHidden` — root inherited value for the form.
|
|
6107
|
+
* 3. Global `withValidationExecutionDefaults()` — fallback when no form/field setting.
|
|
6108
|
+
* 4. No feature configured — uses token default (`validateWhenHidden: false`).
|
|
6109
|
+
*
|
|
6110
|
+
* @param config - Partial override. Unspecified properties keep their defaults.
|
|
6111
|
+
* @returns A `DynamicFormFeature` that configures validation execution defaults.
|
|
6112
|
+
*
|
|
6113
|
+
* @public
|
|
6114
|
+
*/
|
|
6115
|
+
declare function withValidationExecutionDefaults(config?: Partial<ValidationExecutionConfig>): DynamicFormFeature<'validation-execution'>;
|
|
6116
|
+
|
|
6117
|
+
/**
|
|
6118
|
+
* Opt into Angular Signal Forms' legacy `.ng-touched` / `.ng-invalid` /
|
|
6119
|
+
* `.ng-dirty` / `.ng-pristine` / `.ng-untouched` / `.ng-pending` /
|
|
6120
|
+
* `.ng-valid` CSS classes on form-bound elements.
|
|
6121
|
+
*
|
|
6122
|
+
* Wires Angular's `@angular/forms/signals/compat` `NG_STATUS_CLASSES` strategy
|
|
6123
|
+
* via `provideSignalFormsConfig`. Without this feature the modern Signal Forms
|
|
6124
|
+
* class strategy is active, which omits the legacy `ng-*` classes.
|
|
6125
|
+
*
|
|
6126
|
+
* @example Opt in once at the application root
|
|
6127
|
+
* ```typescript
|
|
6128
|
+
* bootstrapApplication(AppComponent, {
|
|
6129
|
+
* providers: [
|
|
6130
|
+
* provideDynamicForm(
|
|
6131
|
+
* ...withMaterialFields(),
|
|
6132
|
+
* withLegacyStatusClasses()
|
|
6133
|
+
* )
|
|
6134
|
+
* ]
|
|
6135
|
+
* });
|
|
6136
|
+
* ```
|
|
6137
|
+
*
|
|
6138
|
+
* @returns A DynamicFormFeature that enables the legacy `ng-*` class strategy
|
|
6139
|
+
*
|
|
6140
|
+
* @public
|
|
6141
|
+
*/
|
|
6142
|
+
declare function withLegacyStatusClasses(): DynamicFormFeature<'legacy-status-classes'>;
|
|
6143
|
+
|
|
6144
|
+
/**
|
|
6145
|
+
* Zero-cost type carrier used in wrapper registrations.
|
|
6146
|
+
*
|
|
6147
|
+
* Returns `undefined` at runtime but is typed as `T`, so TypeScript can
|
|
6148
|
+
* thread a wrapper's config type through `createWrappers(...)` into the
|
|
6149
|
+
* `InferWrapperRegistry<typeof ...>` utility without requiring users to
|
|
6150
|
+
* hand-write the augmentation shape.
|
|
6151
|
+
*
|
|
6152
|
+
* @example
|
|
6153
|
+
* ```typescript
|
|
6154
|
+
* const wrappers = createWrappers(
|
|
6155
|
+
* {
|
|
6156
|
+
* wrapperName: 'section',
|
|
6157
|
+
* loadComponent: () => import('./section-wrapper'),
|
|
6158
|
+
* props: wrapperProps<SectionWrapper>(),
|
|
6159
|
+
* },
|
|
6160
|
+
* );
|
|
6161
|
+
*
|
|
6162
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
6163
|
+
* interface FieldRegistryWrappers extends InferWrapperRegistry<typeof wrappers> {}
|
|
6164
|
+
* }
|
|
6165
|
+
* ```
|
|
6166
|
+
*/
|
|
6167
|
+
declare function wrapperProps<T>(): T;
|
|
6168
|
+
|
|
6169
|
+
/**
|
|
6170
|
+
* Input shape passed to a wrapper component via the `fieldInputs` input.
|
|
6171
|
+
*
|
|
6172
|
+
* Carries the wrapped field's mapper outputs plus an optional `field`
|
|
6173
|
+
* read-only view. Wrappers reached through a container path (which has
|
|
6174
|
+
* no FieldTree of its own) receive `fieldInputs === undefined`, so any
|
|
6175
|
+
* wrapper that reads `fieldInputs.field` must guard for it.
|
|
6176
|
+
*
|
|
6177
|
+
* Mappers must emit new rawInputs objects per tick rather than mutating
|
|
6178
|
+
* the previous one — this bag is a shallow spread, so mutations to shared
|
|
6179
|
+
* nested values leak to downstream wrappers.
|
|
6180
|
+
*/
|
|
6181
|
+
interface WrapperFieldInputs {
|
|
6182
|
+
readonly key: string;
|
|
6183
|
+
/** Field-type discriminant (mirrors `FieldDef.type`) — useful for kind-aware addons. */
|
|
6184
|
+
readonly type?: string;
|
|
6185
|
+
readonly label?: string;
|
|
6186
|
+
readonly placeholder?: string;
|
|
6187
|
+
readonly className?: string;
|
|
6188
|
+
readonly props?: Record<string, unknown>;
|
|
6189
|
+
readonly validationMessages?: Record<string, string>;
|
|
6190
|
+
readonly defaultValidationMessages?: Record<string, string>;
|
|
6191
|
+
readonly field?: ReadonlyFieldTree;
|
|
6192
|
+
/**
|
|
6193
|
+
* Write a value into the wrapped field's tree. Provided by `buildFieldInputs`
|
|
6194
|
+
* when a `FieldTree` is present. Addons (e.g., `prime-button` presets) use this
|
|
6195
|
+
* to mutate the host field; wrappers should treat the field as read-only.
|
|
6196
|
+
*/
|
|
6197
|
+
readonly setValue?: (next: unknown) => void;
|
|
6198
|
+
readonly [key: string]: unknown;
|
|
6199
|
+
}
|
|
6200
|
+
|
|
6201
|
+
/**
|
|
6202
|
+
* Dispatcher component that renders an addon by looking up its `kind` in
|
|
6203
|
+
* `ADDON_KIND_REGISTRY` and instantiating the registered component.
|
|
6204
|
+
*
|
|
6205
|
+
* Used by every adapter's field component to render slot content:
|
|
6206
|
+
*
|
|
6207
|
+
* ```html
|
|
6208
|
+
* \@for (a of addonsAt('prefix'); track $index) {
|
|
6209
|
+
* <df-addon-slot [addon]="a" />
|
|
6210
|
+
* }
|
|
6211
|
+
* ```
|
|
6212
|
+
*
|
|
6213
|
+
* Behaviour:
|
|
6214
|
+
* - Resolves and caches the kind's component asynchronously on first render.
|
|
6215
|
+
* - Forwards the `slot` HTML attribute on the host so Ionic shadow-DOM
|
|
6216
|
+
* projection works (`<ion-input>` projects children with `slot="start"`
|
|
6217
|
+
* into its native start slot).
|
|
6218
|
+
* - Honours the addon's reactive `hidden` flag — when `true`, the host is
|
|
6219
|
+
* `display: none` so it occupies no layout but stays in DOM (cheaper than
|
|
6220
|
+
* tearing down the component).
|
|
6221
|
+
* - Forwards `className` to the host element.
|
|
6222
|
+
* - ARIA semantics are owned by individual kind components — decorative
|
|
6223
|
+
* kinds (icons, text) set `aria-hidden="true"` on themselves; interactive
|
|
6224
|
+
* kinds (buttons) handle their own `aria-label`.
|
|
6225
|
+
*
|
|
6226
|
+
* If the kind cannot be resolved (registry miss, load failure), the
|
|
6227
|
+
* dispatcher logs a warning and renders nothing — never throws. The
|
|
6228
|
+
* runtime addon validator should have caught unknown kinds at config init,
|
|
6229
|
+
* but this provides a defence-in-depth fallback for misconfigured registries.
|
|
6230
|
+
*/
|
|
6231
|
+
declare class DfAddonSlot {
|
|
6232
|
+
private readonly registry;
|
|
6233
|
+
private readonly logger;
|
|
6234
|
+
private readonly hostInjector;
|
|
6235
|
+
readonly addon: _angular_core.InputSignal<AnyAddon>;
|
|
6236
|
+
/**
|
|
6237
|
+
* Optional read-only view of the host field's mapped inputs — same shape
|
|
6238
|
+
* wrappers receive (`field?: ReadonlyFieldTree`, `key`, `props`, etc.).
|
|
6239
|
+
* Forwarded to kind components so they can read field state without
|
|
6240
|
+
* re-injecting `FIELD_SIGNAL_CONTEXT` and rebuilding context themselves.
|
|
6241
|
+
*
|
|
6242
|
+
* `undefined` when the addon is rendered outside a field (rare).
|
|
6243
|
+
*/
|
|
6244
|
+
readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
|
|
6245
|
+
/**
|
|
6246
|
+
* Pre-resolved `hidden` signal supplied by the host field. When provided,
|
|
6247
|
+
* the dispatcher reads this instead of re-resolving `addon.hidden` —
|
|
6248
|
+
* lets the host (e.g., `prime-input`) share a single subscription per
|
|
6249
|
+
* Observable-typed `hidden` between its own visibility filter and the
|
|
6250
|
+
* slot's `[style.display]` binding.
|
|
6251
|
+
*/
|
|
6252
|
+
readonly hidden: _angular_core.InputSignal<Signal<boolean> | undefined>;
|
|
6253
|
+
/** `slot` HTML attribute forwarded to host — needed for Ionic shadow projection. */
|
|
6254
|
+
protected readonly slotAttr: Signal<_ng_forge_dynamic_forms.AddonSlot>;
|
|
6255
|
+
protected readonly className: Signal<string | null>;
|
|
6256
|
+
/**
|
|
6257
|
+
* Locally-resolved `hidden` signal — always reflects `addon.hidden`
|
|
6258
|
+
* regardless of whether the host supplied a pre-resolved signal.
|
|
6259
|
+
* Populated via `explicitEffect` so `resolveDynamicValue` (which may call
|
|
6260
|
+
* `toSignal` for Observable-typed values) does NOT run inside a reactive
|
|
6261
|
+
* context (NG0602). The WeakMap caches per `hidden`-value identity, so
|
|
6262
|
+
* swapping `addon` inputs that share the same Observable reuses the
|
|
6263
|
+
* subscription instead of leaking a fresh one per swap.
|
|
6264
|
+
*
|
|
6265
|
+
* Resolved unconditionally (not gated on host `hidden()` presence) so a
|
|
6266
|
+
* transition from `host hidden = Signal` → `undefined` doesn't expose a
|
|
6267
|
+
* brief window where neither value is current — the upstream effect
|
|
6268
|
+
* re-runs eventually, but the computed read can happen in between.
|
|
6269
|
+
*/
|
|
6270
|
+
private readonly _hiddenByValue;
|
|
6271
|
+
private readonly _resolvedHidden;
|
|
6272
|
+
/**
|
|
6273
|
+
* `hidden` resolved from `DynamicValue<boolean>` to a flat `Signal<boolean>`.
|
|
6274
|
+
* Prefers the host-supplied signal when present (avoids duplicate `toSignal`
|
|
6275
|
+
* subscriptions for Observable-typed `hidden`); falls back to the locally-
|
|
6276
|
+
* resolved signal, which is always populated by the addon-tracking effect.
|
|
6277
|
+
*/
|
|
6278
|
+
protected readonly isHidden: Signal<boolean>;
|
|
6279
|
+
/**
|
|
6280
|
+
* Latest async-loaded `{ kind, component }` pair. Tagging the result with
|
|
6281
|
+
* its kind lets `resolvedComponent` filter stale loads structurally — no
|
|
6282
|
+
* monotonic token needed. When `addon.kind` changes mid-load, the prior
|
|
6283
|
+
* load still completes and writes here, but the computed ignores it
|
|
6284
|
+
* because the recorded kind no longer matches the current one.
|
|
6285
|
+
*/
|
|
6286
|
+
private readonly asyncLoaded;
|
|
6287
|
+
/**
|
|
6288
|
+
* Resolved kind component — composed declaratively from two sources of
|
|
6289
|
+
* truth: the registry's synchronous cache (warm hits, e.g., a second
|
|
6290
|
+
* `<df-addon-slot>` for an already-loaded kind) and the kind-tagged async
|
|
6291
|
+
* loader result. Reading `addon().kind` first makes the computed react to
|
|
6292
|
+
* addon swaps and naturally invalidate stale async-load results.
|
|
6293
|
+
*/
|
|
6294
|
+
protected readonly resolvedComponent: Signal<Type<unknown> | undefined>;
|
|
6295
|
+
/** Inputs passed to the kind component — addon plus the wrapper-style host bag. */
|
|
6296
|
+
protected readonly kindInputs: Signal<{
|
|
6297
|
+
addon: AnyAddon;
|
|
6298
|
+
fieldInputs: WrapperFieldInputs | undefined;
|
|
6299
|
+
}>;
|
|
6300
|
+
constructor();
|
|
6301
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DfAddonSlot, never>;
|
|
6302
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DfAddonSlot, "df-addon-slot", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6303
|
+
}
|
|
6304
|
+
|
|
6305
|
+
/**
|
|
6306
|
+
* Marks an `<ng-template>` projected into `<df-dynamic-form>` content as
|
|
6307
|
+
* available to template addons by name.
|
|
6308
|
+
*
|
|
6309
|
+
* The host form collects all `DfTemplate` instances via `contentChildren`
|
|
6310
|
+
* and exposes them through {@link DF_FIELD_TEMPLATES}, where the template
|
|
6311
|
+
* addon kind looks them up by `templateKey` at render time.
|
|
6312
|
+
*
|
|
6313
|
+
* @example
|
|
6314
|
+
* ```html
|
|
6315
|
+
* <df-dynamic-form [config]="config">
|
|
6316
|
+
* <ng-template dfTemplate="searchIcon" let-field>
|
|
6317
|
+
* <my-icon [field]="field" />
|
|
6318
|
+
* </ng-template>
|
|
6319
|
+
* </df-dynamic-form>
|
|
6320
|
+
* ```
|
|
6321
|
+
*
|
|
6322
|
+
* In the field config:
|
|
6323
|
+
* ```ts
|
|
6324
|
+
* { slot: 'prefix', kind: 'template', templateKey: 'searchIcon' }
|
|
6325
|
+
* ```
|
|
6326
|
+
*/
|
|
6327
|
+
declare class DfTemplate {
|
|
6328
|
+
/** Name this template registers under — referenced from `TemplateAddon.templateKey`. */
|
|
6329
|
+
readonly dfTemplate: _angular_core.InputSignal<string>;
|
|
6330
|
+
/** The wrapped template, captured for registration and `NgTemplateOutlet` rendering. */
|
|
6331
|
+
readonly templateRef: TemplateRef<unknown>;
|
|
6332
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DfTemplate, never>;
|
|
6333
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<DfTemplate, "ng-template[dfTemplate]", ["dfTemplate"], { "dfTemplate": { "alias": "dfTemplate"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6334
|
+
}
|
|
6335
|
+
|
|
6336
|
+
/**
|
|
6337
|
+
* Renderer for the universal `text` addon kind.
|
|
6338
|
+
*
|
|
6339
|
+
* Renders the addon's `text` (a `DynamicText`) inside a span. Decorative —
|
|
6340
|
+
* sets `aria-hidden="true"` on the host so screen readers skip it; users
|
|
6341
|
+
* who need announcement should use a `*-button` kind with `ariaLabel` instead.
|
|
6342
|
+
*/
|
|
6343
|
+
declare class TextAddonComponent {
|
|
6344
|
+
readonly addon: _angular_core.InputSignal<TextAddon>;
|
|
6345
|
+
/** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
|
|
6346
|
+
readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
|
|
6347
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TextAddonComponent, never>;
|
|
6348
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TextAddonComponent, "df-text-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6349
|
+
}
|
|
6350
|
+
|
|
6351
|
+
/**
|
|
6352
|
+
* Renderer for the universal `template` addon kind.
|
|
6353
|
+
*
|
|
6354
|
+
* Resolves the addon's `templateKey` against {@link DF_FIELD_TEMPLATES}
|
|
6355
|
+
* (populated by `<df-dynamic-form>` from `<ng-template dfTemplate="...">`
|
|
6356
|
+
* children) and renders via `NgTemplateOutlet`. The wrapper-style host bag
|
|
6357
|
+
* (`fieldInputs`) is forwarded as the template's implicit context — author
|
|
6358
|
+
* the template as `<ng-template dfTemplate="..." let-fi>{{ fi.key }}</ng-template>`.
|
|
6359
|
+
*
|
|
6360
|
+
* If the key is unresolved, logs a warning and renders nothing — keeps the
|
|
6361
|
+
* form alive when a backend references a template the FE has not registered.
|
|
6362
|
+
*/
|
|
6363
|
+
declare class TemplateAddonComponent {
|
|
6364
|
+
private readonly templates;
|
|
6365
|
+
private readonly logger;
|
|
6366
|
+
readonly addon: _angular_core.InputSignal<TemplateAddon>;
|
|
6367
|
+
/** Forwarded by `df-addon-slot`; surfaced to the template as its implicit `$implicit` context. */
|
|
6368
|
+
readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
|
|
6369
|
+
protected readonly template: _angular_core.Signal<_angular_core.TemplateRef<unknown> | null>;
|
|
6370
|
+
/** Implicit context payload — template authors bind via `let-fi`. */
|
|
6371
|
+
protected readonly outletContext: _angular_core.Signal<{
|
|
6372
|
+
$implicit: WrapperFieldInputs | undefined;
|
|
6373
|
+
}>;
|
|
6374
|
+
constructor();
|
|
6375
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TemplateAddonComponent, never>;
|
|
6376
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TemplateAddonComponent, "df-template-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6377
|
+
}
|
|
6378
|
+
|
|
6379
|
+
/**
|
|
6380
|
+
* Renderer for the universal `component` addon kind.
|
|
6381
|
+
*
|
|
6382
|
+
* Loads the user-supplied component lazily (`addon.component()`) and renders
|
|
6383
|
+
* it via `NgComponentOutlet` with `addon.inputs` forwarded as input bindings.
|
|
6384
|
+
* The wrapper-style `fieldInputs` bag is not propagated — user components
|
|
6385
|
+
* receive only what `addon.inputs` declares. Authors that need field state
|
|
6386
|
+
* should switch to the `template` kind or build a wrapper.
|
|
6387
|
+
*
|
|
6388
|
+
* @codeOnly The `component` loader is a function; this kind is dropped in
|
|
6389
|
+
* lenient validation when the config originated from JSON.
|
|
6390
|
+
*/
|
|
6391
|
+
declare class ComponentAddonComponent {
|
|
6392
|
+
private readonly logger;
|
|
6393
|
+
readonly addon: _angular_core.InputSignal<ComponentAddon>;
|
|
6394
|
+
/** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
|
|
6395
|
+
readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
|
|
6396
|
+
protected readonly inputs: _angular_core.Signal<Record<string, unknown>>;
|
|
6397
|
+
/**
|
|
6398
|
+
* Latest async-loaded `{ addon, component }` pair. Tagging the result with
|
|
6399
|
+
* its source addon lets `resolvedComponent` filter stale loads structurally
|
|
6400
|
+
* — same kind-tagging pattern `DfAddonSlot` uses. When `addon` swaps
|
|
6401
|
+
* mid-load, the prior loader still completes and writes here, but the
|
|
6402
|
+
* computed ignores it because the recorded addon no longer matches the
|
|
6403
|
+
* current one.
|
|
6404
|
+
*/
|
|
6405
|
+
private readonly asyncLoaded;
|
|
6406
|
+
protected readonly resolvedComponent: _angular_core.Signal<Type<unknown> | undefined>;
|
|
6407
|
+
constructor();
|
|
6408
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ComponentAddonComponent, never>;
|
|
6409
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ComponentAddonComponent, "df-component-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6410
|
+
}
|
|
6411
|
+
|
|
6412
|
+
/**
|
|
6413
|
+
* Optional collaborators a preset may need beyond the base context. Passed
|
|
6414
|
+
* by the button kind component which has access to its own DI scope.
|
|
6415
|
+
*
|
|
6416
|
+
* Adapter input components fill in the collaborators their preset semantics
|
|
6417
|
+
* require:
|
|
6418
|
+
* - `mat-input` / `bs-input` / `prime-input` / `ion-input` populate
|
|
6419
|
+
* `typeOverride` + `fieldValueSetter` + `fieldDefaultValueGetter` +
|
|
6420
|
+
* `baselineType` from their own DI scope.
|
|
6421
|
+
* - Addons rendered outside any input field receive `{ logger }` only and
|
|
6422
|
+
* the input-specific presets warn-and-no-op.
|
|
6423
|
+
*/
|
|
6424
|
+
interface PresetCollaborators {
|
|
6425
|
+
/** Per-field input-type override signal — populated only inside an input field. */
|
|
6426
|
+
readonly typeOverride?: WritableSignal<string | undefined>;
|
|
6427
|
+
/** Writer that pushes a value into the host field's tree. */
|
|
6428
|
+
readonly fieldValueSetter?: (next: unknown) => void;
|
|
6429
|
+
/**
|
|
6430
|
+
* Read the host field's configured default value (from the form's
|
|
6431
|
+
* `defaultValues` map keyed by `field.key`). Used by the `'reset'`
|
|
6432
|
+
* preset; absent when the addon is rendered outside an active field
|
|
6433
|
+
* context, in which case `'reset'` falls back to clearing.
|
|
6434
|
+
*/
|
|
6435
|
+
readonly fieldDefaultValueGetter?: () => unknown;
|
|
6436
|
+
/**
|
|
6437
|
+
* Baseline configured input type (from `props().type`), read at preset-run
|
|
6438
|
+
* time. Used by `toggle-password-visibility` to refuse to corrupt
|
|
6439
|
+
* `'email'`/`'number'`/etc. inputs into `'password'` when no override is
|
|
6440
|
+
* yet set.
|
|
6441
|
+
*/
|
|
6442
|
+
readonly baselineType?: () => string | undefined;
|
|
6443
|
+
/**
|
|
6444
|
+
* Logger for warnings about presets that can't be fulfilled in this context.
|
|
6445
|
+
*
|
|
6446
|
+
* Passed via collaborators (rather than injected) because `runPresetAction`
|
|
6447
|
+
* is a DI-free pure function shared across adapters — each adapter's
|
|
6448
|
+
* `ADDON_PRESET_HANDLER` factory injects `DynamicFormLogger` itself and
|
|
6449
|
+
* forwards it here. The `[Dynamic Forms]` prefix comes from
|
|
6450
|
+
* `ConsoleLogger`'s implementation, so the final console line is identical
|
|
6451
|
+
* to one logged directly via `inject(DynamicFormLogger)`.
|
|
6452
|
+
*/
|
|
6453
|
+
readonly logger: Logger;
|
|
6454
|
+
}
|
|
6455
|
+
/**
|
|
6456
|
+
* Apply a built-in preset action against the addon's host field. Shared
|
|
6457
|
+
* across all adapters — each adapter's `ADDON_PRESET_HANDLER` provider
|
|
6458
|
+
* delegates here.
|
|
6459
|
+
*
|
|
6460
|
+
* Presets that require adapter-specific UI mechanics (clipboard, type
|
|
6461
|
+
* override) are best-effort — they no-op with a warning when the necessary
|
|
6462
|
+
* collaborator isn't available (e.g., toggle-password-visibility outside an
|
|
6463
|
+
* input field).
|
|
6464
|
+
*
|
|
6465
|
+
* @param adapterLabel Used in warnings — `'Material'` / `'Bootstrap'` / etc.
|
|
6466
|
+
* @param fieldLabel Used in warnings — `'mat-input'` / `'bs-input'` / etc.
|
|
6467
|
+
*/
|
|
6468
|
+
declare function runPresetAction(preset: AddonActionPreset, ctx: AddonActionContext, collaborators: PresetCollaborators, adapterLabel: string, fieldLabel: string): Promise<void>;
|
|
6469
|
+
|
|
6470
|
+
/**
|
|
6471
|
+
* Cache for resolved addon-kind components.
|
|
6472
|
+
*
|
|
6473
|
+
* Provided per-form by `provideDynamicFormDI()` so two `<df-dynamic-form>`
|
|
6474
|
+
* instances with conflicting `loadComponent` factories for the same kind
|
|
6475
|
+
* never share a stale entry. Has no `providedIn: 'root'` factory — the
|
|
6476
|
+
* registry consumer is always inside a form scope, and a root fallback
|
|
6477
|
+
* would only hide misconfiguration (an SSR cross-request hit if a future
|
|
6478
|
+
* code path forgot to provide it locally).
|
|
6479
|
+
*
|
|
6480
|
+
* Mirrors `COMPONENT_CACHE` (field types) and `WRAPPER_COMPONENT_CACHE`
|
|
6481
|
+
* (wrappers).
|
|
6482
|
+
*
|
|
6483
|
+
* @internal
|
|
6484
|
+
*/
|
|
6485
|
+
declare const ADDON_KIND_COMPONENT_CACHE: InjectionToken<Map<string, Type<unknown>>>;
|
|
6486
|
+
/**
|
|
6487
|
+
* Public shape returned by {@link injectAddonKindRegistry}. Pinned explicitly
|
|
6488
|
+
* so the API extractor doesn't churn between versions when the inner factory
|
|
6489
|
+
* gets refactored.
|
|
6490
|
+
*/
|
|
6491
|
+
interface AddonKindRegistryRef {
|
|
6492
|
+
getKind(kind: string): AddonKindDefinition | undefined;
|
|
6493
|
+
hasKind(kind: string): boolean;
|
|
6494
|
+
getKindNames(): string[];
|
|
6495
|
+
loadKindComponent(kind: string): Promise<Type<unknown>>;
|
|
6496
|
+
getLoadedKindComponent(kind: string): Type<unknown> | undefined;
|
|
6497
|
+
readonly raw: ReadonlyMap<string, AddonKindDefinition>;
|
|
6498
|
+
}
|
|
6499
|
+
/**
|
|
6500
|
+
* Injection function for accessing the addon kind registry.
|
|
5496
6501
|
*
|
|
5497
|
-
* @
|
|
6502
|
+
* Usage parallels {@link injectFieldRegistry}: lookup, lazy load, cache.
|
|
6503
|
+
* Must be called within an injection context.
|
|
6504
|
+
*
|
|
6505
|
+
* @example
|
|
6506
|
+
* ```typescript
|
|
6507
|
+
* const addonKinds = injectAddonKindRegistry();
|
|
6508
|
+
* const Component = await addonKinds.loadKindComponent('prime-icon');
|
|
6509
|
+
* ```
|
|
5498
6510
|
*/
|
|
5499
|
-
|
|
6511
|
+
declare function injectAddonKindRegistry(): AddonKindRegistryRef;
|
|
5500
6512
|
|
|
5501
6513
|
/**
|
|
5502
|
-
*
|
|
6514
|
+
* One entry per field type that declares addon support.
|
|
5503
6515
|
*
|
|
5504
|
-
*
|
|
5505
|
-
* `
|
|
5506
|
-
|
|
5507
|
-
|
|
6516
|
+
* `name` is the field-type discriminant; `slots` and `allowedKinds` mirror
|
|
6517
|
+
* the `FieldTypeDefinition.addons` registration.
|
|
6518
|
+
*/
|
|
6519
|
+
interface FieldAddonSupportEntry {
|
|
6520
|
+
readonly name: string;
|
|
6521
|
+
readonly slots: FieldAddonSupport['slots'];
|
|
6522
|
+
readonly allowedKinds: FieldAddonSupport['allowedKinds'];
|
|
6523
|
+
}
|
|
6524
|
+
/**
|
|
6525
|
+
* Walk `FIELD_REGISTRY` and return every field type that opted into addons
|
|
6526
|
+
* (i.e., declared `addons` on its `FieldTypeDefinition`).
|
|
5508
6527
|
*
|
|
5509
|
-
*
|
|
6528
|
+
* Useful for tooling — admin UIs surfacing "which fields can carry addons?",
|
|
6529
|
+
* docs generators, MCP server cross-checks. Field types without an `addons`
|
|
6530
|
+
* registration ("Tier 3" — toggle, checkbox, radio, slider) are excluded.
|
|
6531
|
+
*
|
|
6532
|
+
* Must be called within an injection context.
|
|
6533
|
+
*
|
|
6534
|
+
* @example
|
|
5510
6535
|
* ```typescript
|
|
5511
|
-
*
|
|
5512
|
-
*
|
|
5513
|
-
*
|
|
5514
|
-
*
|
|
6536
|
+
* @Component({ ... })
|
|
6537
|
+
* class FormBuilder {
|
|
6538
|
+
* protected readonly addonable = injectFieldsSupportingAddons();
|
|
6539
|
+
*
|
|
6540
|
+
* constructor() {
|
|
6541
|
+
* // -> [{ name: 'prime-input', slots: ['prefix', 'suffix'], allowedKinds: [...] }, ...]
|
|
6542
|
+
* }
|
|
6543
|
+
* }
|
|
5515
6544
|
* ```
|
|
6545
|
+
*/
|
|
6546
|
+
declare function injectFieldsSupportingAddons(): FieldAddonSupportEntry[];
|
|
6547
|
+
|
|
6548
|
+
/**
|
|
6549
|
+
* Normalises a {@link DynamicValue} into a `Signal<T>`.
|
|
5516
6550
|
*
|
|
5517
|
-
*
|
|
6551
|
+
* Accepts a static value, a `Signal<T>`, or an `Observable<T>`. The result
|
|
6552
|
+
* is a `Signal<T>` that reactively reflects the current value, suitable
|
|
6553
|
+
* for direct use in Angular templates.
|
|
6554
|
+
*
|
|
6555
|
+
* Static values yield a `computed` that always returns the same constant —
|
|
6556
|
+
* preferable to wrapping in `signal()` because the computed never causes
|
|
6557
|
+
* change-detection invalidation.
|
|
6558
|
+
*
|
|
6559
|
+
* Pass an `injector` when calling outside an Angular injection context
|
|
6560
|
+
* (e.g., from inside a `computed`). `toSignal` needs a `DestroyRef` to
|
|
6561
|
+
* unsubscribe — without an injector it falls back to `inject(DestroyRef)`,
|
|
6562
|
+
* which throws when called outside injection context.
|
|
6563
|
+
*
|
|
6564
|
+
* @example
|
|
5518
6565
|
* ```typescript
|
|
5519
|
-
*
|
|
5520
|
-
*
|
|
5521
|
-
* withValueExclusionDefaults({ excludeValueIfReadonly: false })
|
|
5522
|
-
* )
|
|
6566
|
+
* const isHidden = resolveDynamicValue(addon().hidden, false);
|
|
6567
|
+
* // template: \@if (isHidden()) { ... }
|
|
5523
6568
|
* ```
|
|
6569
|
+
*/
|
|
6570
|
+
declare function resolveDynamicValue<T>(value: DynamicValue<T> | undefined, fallback: T, injector?: Injector): Signal<T>;
|
|
6571
|
+
|
|
6572
|
+
/**
|
|
6573
|
+
* Register a custom addon kind with the form.
|
|
5524
6574
|
*
|
|
5525
|
-
*
|
|
6575
|
+
* The provided definition is added to `ADDON_KIND_REGISTRY` at form
|
|
6576
|
+
* initialisation, alongside the built-in `text` / `template` / `component`
|
|
6577
|
+
* kinds and any kinds contributed by adapter feature helpers
|
|
6578
|
+
* (`withPrimeNGAddons()` etc.).
|
|
6579
|
+
*
|
|
6580
|
+
* @example
|
|
5526
6581
|
* ```typescript
|
|
5527
6582
|
* provideDynamicForm(
|
|
5528
6583
|
* ...withMaterialFields(),
|
|
5529
|
-
*
|
|
5530
|
-
*
|
|
5531
|
-
*
|
|
5532
|
-
*
|
|
5533
|
-
*
|
|
5534
|
-
* )
|
|
6584
|
+
* withCustomAddon({
|
|
6585
|
+
* kind: 'lucide-icon',
|
|
6586
|
+
* // Match how field types register: a default-exported component lets
|
|
6587
|
+
* // `loadComponent` return the import promise directly.
|
|
6588
|
+
* loadComponent: () => import('./lucide-icon-addon.component'),
|
|
6589
|
+
* validate: (addon) => {
|
|
6590
|
+
* if (typeof addon.icon !== 'string') {
|
|
6591
|
+
* throw new Error('lucide-icon: icon string is required');
|
|
6592
|
+
* }
|
|
6593
|
+
* },
|
|
6594
|
+
* }),
|
|
6595
|
+
* );
|
|
5535
6596
|
* ```
|
|
5536
6597
|
*
|
|
5537
|
-
*
|
|
5538
|
-
*
|
|
5539
|
-
* 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
|
|
5540
|
-
* 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
|
|
5541
|
-
* 3. Global `withValueExclusionDefaults()` — baseline default
|
|
5542
|
-
* 4. No global feature — uses token default (all enabled)
|
|
5543
|
-
*
|
|
5544
|
-
* @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
|
|
5545
|
-
* @returns A DynamicFormFeature that configures value exclusion defaults
|
|
5546
|
-
*
|
|
5547
|
-
* @public
|
|
6598
|
+
* Multiple `withCustomAddon(...)` calls are supported; later registrations
|
|
6599
|
+
* for the same kind override earlier ones (with a warning logged).
|
|
5548
6600
|
*/
|
|
5549
|
-
declare function
|
|
6601
|
+
declare function withCustomAddon<T extends BaseAddon>(definition: AddonKindDefinition<T>): DynamicFormFeature<'addons'>;
|
|
5550
6602
|
|
|
5551
6603
|
/**
|
|
5552
|
-
*
|
|
5553
|
-
*
|
|
5554
|
-
* Currently controls whether hidden fields are validated. Resolved through a
|
|
5555
|
-
* tree-aware hierarchy: per-field overrides inherit down to descendants, with
|
|
5556
|
-
* per-form `FormOptions` and global `withValidationExecutionDefaults()` providing
|
|
5557
|
-
* the root inherited value.
|
|
6604
|
+
* Handler signature for user-registered addon actions.
|
|
5558
6605
|
*
|
|
5559
|
-
*
|
|
6606
|
+
* Backends reference handlers by name via `actionRef: 'name'` on button-style
|
|
6607
|
+
* addons; the dispatcher invokes the registered function with the addon's
|
|
6608
|
+
* action context.
|
|
5560
6609
|
*/
|
|
5561
|
-
|
|
5562
|
-
/**
|
|
5563
|
-
* Whether validators run while a field is hidden (statically, via `hidden`
|
|
5564
|
-
* logic, or because any ancestor is hidden).
|
|
5565
|
-
*
|
|
5566
|
-
* When `false`, all validators on a hidden field are skipped — the field is
|
|
5567
|
-
* effectively inert until shown. State logic (hidden/disabled/readonly) and
|
|
5568
|
-
* value derivations continue to apply.
|
|
5569
|
-
*/
|
|
5570
|
-
readonly validateWhenHidden?: boolean;
|
|
5571
|
-
}
|
|
6610
|
+
type AddonActionHandler<TValue = unknown> = (ctx: AddonActionContext<TValue>) => void;
|
|
5572
6611
|
/**
|
|
5573
|
-
*
|
|
6612
|
+
* Multi-provider token populated by `provideAddonActions({...})` calls.
|
|
6613
|
+
* `ADDON_ACTION_REGISTRY` aggregates these into a single name → handler map.
|
|
5574
6614
|
*
|
|
5575
|
-
* @
|
|
6615
|
+
* @internal
|
|
5576
6616
|
*/
|
|
5577
|
-
|
|
6617
|
+
declare const ADDON_ACTION_HANDLERS: InjectionToken<readonly Record<string, AddonActionHandler<unknown>>[]>;
|
|
6618
|
+
/**
|
|
6619
|
+
* Form-scoped name → handler map. Read by adapter button kinds at click
|
|
6620
|
+
* time for `actionRef: 'name'` lookups. Provided at form scope (not root)
|
|
6621
|
+
* because `ADDON_ACTION_HANDLERS` is form-scoped via `provideAddonActions`.
|
|
6622
|
+
*/
|
|
6623
|
+
declare const ADDON_ACTION_REGISTRY: InjectionToken<ReadonlyMap<string, AddonActionHandler<unknown>>>;
|
|
5578
6624
|
|
|
5579
6625
|
/**
|
|
5580
|
-
*
|
|
5581
|
-
*
|
|
6626
|
+
* Typed feature returned by {@link provideAddonActions}. Carries the
|
|
6627
|
+
* registered handler keys as a phantom type so consumers can derive the
|
|
6628
|
+
* `DynamicFormActionRegistry` augmentation in one line instead of hand-typing
|
|
6629
|
+
* every key.
|
|
5582
6630
|
*
|
|
5583
|
-
*
|
|
5584
|
-
*
|
|
5585
|
-
|
|
6631
|
+
* Phantom field is `__handlerKeys` (the leading underscores signal "internal,
|
|
6632
|
+
* type-only — not for runtime access").
|
|
6633
|
+
*/
|
|
6634
|
+
type AddonActionsFeature<K extends string = string> = DynamicFormFeature<'addon-actions'> & {
|
|
6635
|
+
/** @internal Phantom field carrying the registered keys; never read at runtime. */
|
|
6636
|
+
readonly __handlerKeys?: K;
|
|
6637
|
+
};
|
|
6638
|
+
/**
|
|
6639
|
+
* Register named action handlers reachable via JSON-driven addon configs.
|
|
5586
6640
|
*
|
|
5587
|
-
*
|
|
5588
|
-
*
|
|
5589
|
-
*
|
|
5590
|
-
*
|
|
5591
|
-
*
|
|
6641
|
+
* Once registered, a backend can ship
|
|
6642
|
+
* `{ kind: 'prime-button', actionRef: 'openProfile' }` and the FE will invoke
|
|
6643
|
+
* the corresponding handler at click time.
|
|
6644
|
+
*
|
|
6645
|
+
* Define handlers once and augment the registry from the same object:
|
|
5592
6646
|
*
|
|
5593
|
-
* @example Restore legacy validation-on-hidden behavior
|
|
5594
6647
|
* ```typescript
|
|
5595
|
-
*
|
|
5596
|
-
*
|
|
5597
|
-
*
|
|
5598
|
-
* )
|
|
6648
|
+
* export const appActions = provideAddonActions({
|
|
6649
|
+
* openProfile: (ctx) => modalService.open(ProfileModal, { data: ctx.value }),
|
|
6650
|
+
* runWorkflow: (ctx) => workflowService.run(ctx.field.key, ctx.value),
|
|
6651
|
+
* });
|
|
6652
|
+
*
|
|
6653
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
6654
|
+
* // Pulls the keys straight off `appActions` — no manual list to keep in sync.
|
|
6655
|
+
* interface DynamicFormActionRegistry extends Record<NonNullable<typeof appActions['__handlerKeys']>, true> {}
|
|
6656
|
+
* }
|
|
6657
|
+
*
|
|
6658
|
+
* // Then in your bootstrap:
|
|
6659
|
+
* provideDynamicForm(...withPrimeNGFields(), appActions);
|
|
5599
6660
|
* ```
|
|
5600
6661
|
*
|
|
5601
|
-
*
|
|
5602
|
-
*
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
*
|
|
6662
|
+
* Multiple `provideAddonActions(...)` calls are merged; later names override
|
|
6663
|
+
* earlier ones for the same key.
|
|
6664
|
+
*/
|
|
6665
|
+
declare function provideAddonActions<const H extends Record<string, AddonActionHandler>>(handlers: H): AddonActionsFeature<keyof H & string>;
|
|
6666
|
+
|
|
6667
|
+
/**
|
|
6668
|
+
* Multi-provider token used by `withCustomAddon(...)` features to contribute
|
|
6669
|
+
* addon kind definitions. `provideDynamicForm` aggregates all values into
|
|
6670
|
+
* the form-scoped `ADDON_KIND_REGISTRY` map.
|
|
5608
6671
|
*
|
|
5609
|
-
* @
|
|
5610
|
-
|
|
6672
|
+
* @internal
|
|
6673
|
+
*/
|
|
6674
|
+
declare const ADDON_KIND_DEFINITIONS: InjectionToken<readonly AddonKindDefinition<_ng_forge_dynamic_forms.BaseAddon<_ng_forge_dynamic_forms.AddonSlot>>[]>;
|
|
6675
|
+
|
|
6676
|
+
/**
|
|
6677
|
+
* Reason an addon was dropped during validation.
|
|
5611
6678
|
*
|
|
5612
|
-
*
|
|
6679
|
+
* Each variant carries its own context fields so log messages can be both
|
|
6680
|
+
* actionable (registered kinds enumerated, fix suggestion present) and
|
|
6681
|
+
* machine-readable (admin UIs surfacing per-error guidance).
|
|
5613
6682
|
*/
|
|
5614
|
-
|
|
6683
|
+
type AddonWarning = {
|
|
6684
|
+
type: 'unknown-field-type';
|
|
6685
|
+
fieldKey: string;
|
|
6686
|
+
fieldType: string;
|
|
6687
|
+
} | {
|
|
6688
|
+
type: 'field-type-no-addon-support';
|
|
6689
|
+
fieldKey: string;
|
|
6690
|
+
fieldType: string;
|
|
6691
|
+
} | {
|
|
6692
|
+
type: 'unknown-slot';
|
|
6693
|
+
fieldKey: string;
|
|
6694
|
+
fieldType: string;
|
|
6695
|
+
slot: string;
|
|
6696
|
+
allowedSlots: readonly string[];
|
|
6697
|
+
} | {
|
|
6698
|
+
type: 'unknown-kind';
|
|
6699
|
+
fieldKey: string;
|
|
6700
|
+
kind: string;
|
|
6701
|
+
registeredKinds: readonly string[];
|
|
6702
|
+
} | {
|
|
6703
|
+
type: 'kind-not-allowed';
|
|
6704
|
+
fieldKey: string;
|
|
6705
|
+
fieldType: string;
|
|
6706
|
+
kind: string;
|
|
6707
|
+
allowedKinds: readonly string[];
|
|
6708
|
+
} | {
|
|
6709
|
+
type: 'shape-violation';
|
|
6710
|
+
fieldKey: string;
|
|
6711
|
+
kind: string;
|
|
6712
|
+
reason: string;
|
|
6713
|
+
} | {
|
|
6714
|
+
type: 'code-only-kind-in-json';
|
|
6715
|
+
fieldKey: string;
|
|
6716
|
+
kind: string;
|
|
6717
|
+
} | {
|
|
6718
|
+
type: 'code-only-action-in-json';
|
|
6719
|
+
fieldKey: string;
|
|
6720
|
+
reason: string;
|
|
6721
|
+
};
|
|
6722
|
+
/**
|
|
6723
|
+
* Render an `AddonWarning` to a developer-friendly message.
|
|
6724
|
+
*
|
|
6725
|
+
* Returns the bare message — the `[Dynamic Forms]` prefix is added by the
|
|
6726
|
+
* library's logger (`ConsoleLogger`) and by `DynamicFormError`. Pass the
|
|
6727
|
+
* result of this function directly into `logger.warn(...)` to get the
|
|
6728
|
+
* standard prefixed line; use `logAddonWarnings()` for direct
|
|
6729
|
+
* `console.warn` output (which prefixes manually).
|
|
6730
|
+
*
|
|
6731
|
+
* Messages are designed to be actionable: they enumerate the relevant set
|
|
6732
|
+
* (registered kinds, allowed slots) and suggest a likely fix.
|
|
6733
|
+
*/
|
|
6734
|
+
declare function formatAddonWarning(w: AddonWarning): string;
|
|
5615
6735
|
|
|
5616
6736
|
/**
|
|
5617
|
-
*
|
|
6737
|
+
* Walk a field's addons; return the survivors plus a list of warnings for
|
|
6738
|
+
* anything dropped.
|
|
5618
6739
|
*
|
|
5619
|
-
*
|
|
5620
|
-
*
|
|
5621
|
-
*
|
|
5622
|
-
*
|
|
6740
|
+
* Pure: receives the registries as arguments (so the function is reusable
|
|
6741
|
+
* outside Angular's DI — tests, server-side validation, etc.).
|
|
6742
|
+
*
|
|
6743
|
+
* @param field The field to validate.
|
|
6744
|
+
* @param fieldRegistry Snapshot of `FIELD_REGISTRY` map.
|
|
6745
|
+
* @param kindRegistry Snapshot of `ADDON_KIND_REGISTRY` map.
|
|
6746
|
+
* @param source Whether the config originated from JSON (lenient drop
|
|
6747
|
+
* of code-only kinds) or inline TypeScript.
|
|
6748
|
+
*/
|
|
6749
|
+
declare function validateFieldAddons(field: FieldDef<unknown>, fieldRegistry: ReadonlyMap<string, FieldTypeDefinition>, kindRegistry: ReadonlyMap<string, AddonKindDefinition>, source: 'json' | 'inline'): {
|
|
6750
|
+
addons: AnyAddon[];
|
|
6751
|
+
warnings: AddonWarning[];
|
|
6752
|
+
};
|
|
6753
|
+
/**
|
|
6754
|
+
* Recursively walk a field tree, validating each field's addons. Returns a
|
|
6755
|
+
* sanitized copy of the input field array plus accumulated warnings.
|
|
6756
|
+
*/
|
|
6757
|
+
declare function walkAndValidateAddons(fields: readonly FieldDef<unknown>[], fieldRegistry: ReadonlyMap<string, FieldTypeDefinition>, kindRegistry: ReadonlyMap<string, AddonKindDefinition>, source: 'json' | 'inline'): {
|
|
6758
|
+
fields: FieldDef<unknown>[];
|
|
6759
|
+
warnings: AddonWarning[];
|
|
6760
|
+
};
|
|
6761
|
+
|
|
6762
|
+
/**
|
|
6763
|
+
* Options accepted by {@link sanitizeFormConfig}.
|
|
6764
|
+
*/
|
|
6765
|
+
interface SanitizeFormConfigOptions {
|
|
6766
|
+
/**
|
|
6767
|
+
* Whether the config originated from JSON (server-driven) or inline
|
|
6768
|
+
* TypeScript code.
|
|
6769
|
+
*
|
|
6770
|
+
* - `'inline'` (default): keeps everything; assumes the config is
|
|
6771
|
+
* authored in code and code-only constructs (inline `action`
|
|
6772
|
+
* functions, `component` kind) are intentional. The default avoids
|
|
6773
|
+
* silently stripping inline behaviour during in-process authoring.
|
|
6774
|
+
* - `'json'`: drops code-only addon kinds (`component`, inline
|
|
6775
|
+
* `action: function`) with a warning, since they cannot survive
|
|
6776
|
+
* serialization. Use this when sanitising configs received from a
|
|
6777
|
+
* backend / form-builder UI.
|
|
6778
|
+
*/
|
|
6779
|
+
source?: 'json' | 'inline';
|
|
6780
|
+
}
|
|
6781
|
+
/**
|
|
6782
|
+
* Result of a `sanitizeFormConfig` call.
|
|
6783
|
+
*/
|
|
6784
|
+
interface SanitizedFormConfig {
|
|
6785
|
+
/** Sanitized config — invalid addons stripped from the field tree. */
|
|
6786
|
+
sanitized: FormConfig;
|
|
6787
|
+
/** Structured list of every addon that was dropped, and why. */
|
|
6788
|
+
warnings: readonly AddonWarning[];
|
|
6789
|
+
}
|
|
6790
|
+
/**
|
|
6791
|
+
* Walk a `FormConfig`, validate every field's addons against the active
|
|
6792
|
+
* `FIELD_REGISTRY` and `ADDON_KIND_REGISTRY`, and return a sanitized copy
|
|
6793
|
+
* plus a list of dropped-addon warnings.
|
|
6794
|
+
*
|
|
6795
|
+
* Distinct from the INTERNAL `validateFormConfig` in
|
|
6796
|
+
* `utils/config-validation/config-validator.ts` (which throws on
|
|
6797
|
+
* structural errors like duplicate keys / unknown field types). This
|
|
6798
|
+
* helper is the PUBLIC, lenient addon-shape pass — renamed to
|
|
6799
|
+
* `sanitizeFormConfig` to avoid the name collision.
|
|
6800
|
+
*
|
|
6801
|
+
* Lenient by design: invalid addons are dropped (with warnings) rather than
|
|
6802
|
+
* thrown. The form keeps rendering even when the backend ships an addon the
|
|
6803
|
+
* FE doesn't understand.
|
|
6804
|
+
*
|
|
6805
|
+
* `<df-dynamic-form>` already runs this internally at form init — calling
|
|
6806
|
+
* the public helper AND passing the original config to the form will
|
|
6807
|
+
* double-emit warnings. Use this entry point only when you need the
|
|
6808
|
+
* sanitized config or warning list outside the form (e.g., admin-UI
|
|
6809
|
+
* diagnostics, build-time lints).
|
|
6810
|
+
*
|
|
6811
|
+
* Must be called within an Angular injection context — typically inside
|
|
6812
|
+
* an admin-UI component or a `runInInjectionContext()` block on the server.
|
|
5623
6813
|
*
|
|
5624
6814
|
* @example
|
|
5625
6815
|
* ```typescript
|
|
5626
|
-
*
|
|
5627
|
-
*
|
|
5628
|
-
*
|
|
5629
|
-
*
|
|
5630
|
-
*
|
|
5631
|
-
*
|
|
5632
|
-
*
|
|
5633
|
-
*
|
|
5634
|
-
*
|
|
5635
|
-
* interface FieldRegistryWrappers extends InferWrapperRegistry<typeof wrappers> {}
|
|
6816
|
+
* @Component({ ... })
|
|
6817
|
+
* class AdminFormBuilder {
|
|
6818
|
+
* private readonly check = (cfg: FormConfig) => {
|
|
6819
|
+
* const { sanitized, warnings } = sanitizeFormConfig(cfg, { source: 'json' });
|
|
6820
|
+
* if (warnings.length > 0) {
|
|
6821
|
+
* this.diagnostics.set(warnings);
|
|
6822
|
+
* }
|
|
6823
|
+
* return sanitized;
|
|
6824
|
+
* };
|
|
5636
6825
|
* }
|
|
5637
6826
|
* ```
|
|
5638
6827
|
*/
|
|
5639
|
-
declare function
|
|
6828
|
+
declare function sanitizeFormConfig(config: FormConfig, options?: SanitizeFormConfigOptions): SanitizedFormConfig;
|
|
6829
|
+
/**
|
|
6830
|
+
* Default warning sink. Pass a `DynamicFormLogger` to route through the
|
|
6831
|
+
* form's configured logger (respects `NoopLogger` opt-out); omit it to
|
|
6832
|
+
* fall back to `console.warn` for build-time / admin-UI use cases that
|
|
6833
|
+
* run outside an Angular injection context.
|
|
6834
|
+
*/
|
|
6835
|
+
declare function logAddonWarnings(warnings: readonly AddonWarning[], logger?: Logger): void;
|
|
6836
|
+
/**
|
|
6837
|
+
* DI-free variant of {@link sanitizeFormConfig} for tooling that lives
|
|
6838
|
+
* outside Angular's injector — build-time linters, MCP-side checks, CLI
|
|
6839
|
+
* scripts.
|
|
6840
|
+
*
|
|
6841
|
+
* Caller passes the registries explicitly; the result has the same shape
|
|
6842
|
+
* as the DI-bound version.
|
|
6843
|
+
*/
|
|
6844
|
+
declare function sanitizeFormConfigPure(config: FormConfig, fieldRegistry: ReadonlyMap<string, FieldTypeDefinition>, kindRegistry: ReadonlyMap<string, AddonKindDefinition>, options?: SanitizeFormConfigOptions): SanitizedFormConfig;
|
|
5640
6845
|
|
|
5641
6846
|
interface FieldWithValidation {
|
|
5642
6847
|
readonly required?: boolean;
|
|
@@ -5722,7 +6927,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TN
|
|
|
5722
6927
|
nullable?: TNullable;
|
|
5723
6928
|
}
|
|
5724
6929
|
declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
|
|
5725
|
-
type ExcludedKeys$1 = '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';
|
|
6930
|
+
type ExcludedKeys$1 = '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';
|
|
5726
6931
|
type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
|
|
5727
6932
|
|
|
5728
6933
|
interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends FieldDef<TProps, TMeta>, FieldWithValidation {
|
|
@@ -5735,7 +6940,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends
|
|
|
5735
6940
|
required?: boolean;
|
|
5736
6941
|
}
|
|
5737
6942
|
declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta>;
|
|
5738
|
-
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers';
|
|
6943
|
+
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
|
|
5739
6944
|
type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
|
|
5740
6945
|
|
|
5741
6946
|
declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
|
|
@@ -6852,71 +8057,6 @@ declare class ContainerFieldComponent {
|
|
|
6852
8057
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContainerFieldComponent, "div[container-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "hidden": { "alias": "hidden"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
6853
8058
|
}
|
|
6854
8059
|
|
|
6855
|
-
/**
|
|
6856
|
-
* Type representing a FieldTree for an array.
|
|
6857
|
-
* Extends FieldTree to add numeric indexing for accessing item FieldTrees.
|
|
6858
|
-
*/
|
|
6859
|
-
type ArrayFieldTree<T> = FieldTree<T[]> & {
|
|
6860
|
-
readonly [index: number]: FieldTree<T> | undefined;
|
|
6861
|
-
};
|
|
6862
|
-
/**
|
|
6863
|
-
* Get the length of an array FieldTree.
|
|
6864
|
-
*/
|
|
6865
|
-
declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
|
|
6866
|
-
/**
|
|
6867
|
-
* Read-only view of a single field's observable state.
|
|
6868
|
-
*
|
|
6869
|
-
* Whitelisted read signals copied from Angular Signal Forms' `FieldState` so wrappers
|
|
6870
|
-
* can observe a field without being able to mutate it. New write-capable members
|
|
6871
|
-
* added in future Angular versions are excluded by default (the `Pick` list stays
|
|
6872
|
-
* the source of truth).
|
|
6873
|
-
*
|
|
6874
|
-
* `value` is narrowed from `WritableSignal<TValue>` to `Signal<TValue>` so consumers
|
|
6875
|
-
* cannot write through it.
|
|
6876
|
-
*/
|
|
6877
|
-
interface ReadonlyFieldTree<TValue = unknown> {
|
|
6878
|
-
readonly value: Signal<TValue>;
|
|
6879
|
-
readonly valid: Signal<boolean>;
|
|
6880
|
-
readonly invalid: Signal<boolean>;
|
|
6881
|
-
readonly touched: Signal<boolean>;
|
|
6882
|
-
readonly dirty: Signal<boolean>;
|
|
6883
|
-
readonly required: Signal<boolean>;
|
|
6884
|
-
readonly disabled: Signal<boolean>;
|
|
6885
|
-
readonly hidden: Signal<boolean>;
|
|
6886
|
-
readonly errors: Signal<readonly unknown[]>;
|
|
6887
|
-
}
|
|
6888
|
-
/**
|
|
6889
|
-
* Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
|
|
6890
|
-
* Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
|
|
6891
|
-
* so consumers only see the narrow surface and Angular's `WritableSignal` capability
|
|
6892
|
-
* on `value` is hidden.
|
|
6893
|
-
*/
|
|
6894
|
-
declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
|
|
6895
|
-
|
|
6896
|
-
/**
|
|
6897
|
-
* Input shape passed to a wrapper component via the `fieldInputs` input.
|
|
6898
|
-
*
|
|
6899
|
-
* Carries the wrapped field's mapper outputs plus an optional `field`
|
|
6900
|
-
* read-only view. Wrappers reached through a container path (which has
|
|
6901
|
-
* no FieldTree of its own) receive `fieldInputs === undefined`, so any
|
|
6902
|
-
* wrapper that reads `fieldInputs.field` must guard for it.
|
|
6903
|
-
*
|
|
6904
|
-
* Mappers must emit new rawInputs objects per tick rather than mutating
|
|
6905
|
-
* the previous one — this bag is a shallow spread, so mutations to shared
|
|
6906
|
-
* nested values leak to downstream wrappers.
|
|
6907
|
-
*/
|
|
6908
|
-
interface WrapperFieldInputs {
|
|
6909
|
-
readonly key: string;
|
|
6910
|
-
readonly label?: string;
|
|
6911
|
-
readonly placeholder?: string;
|
|
6912
|
-
readonly className?: string;
|
|
6913
|
-
readonly props?: Record<string, unknown>;
|
|
6914
|
-
readonly validationMessages?: Record<string, string>;
|
|
6915
|
-
readonly defaultValidationMessages?: Record<string, string>;
|
|
6916
|
-
readonly field?: ReadonlyFieldTree;
|
|
6917
|
-
readonly [key: string]: unknown;
|
|
6918
|
-
}
|
|
6919
|
-
|
|
6920
8060
|
/**
|
|
6921
8061
|
* Native JavaScript utility functions to replace lodash-es
|
|
6922
8062
|
* These are simple implementations for common object manipulation patterns
|
|
@@ -6995,5 +8135,5 @@ declare class RootFormRegistryService {
|
|
|
6995
8135
|
*/
|
|
6996
8136
|
declare const INITIALIZATION_TIMEOUT_MS: InjectionToken<number>;
|
|
6997
8137
|
|
|
6998
|
-
export { ARRAY_CONTEXT, AppendArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, ContainerFieldComponent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, EventDispatcher, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GROUP_CONTEXT, GroupFieldComponent, INITIALIZATION_TIMEOUT_MS, InsertArrayItemEvent, MoveArrayItemEvent, NextPageEvent, NoopLogger, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, RootFormRegistryService, ShiftArrayItemEvent, SubmitEvent, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayEvent, arrayFieldMapper, baseFieldMapper, buildBaseInputs, containerFieldMapper, createField, createWrappers, dynamicTextToObservable, evaluateNonFieldDisabled, evaluateNonFieldHidden, field, formConfig, getArrayLength, groupFieldMapper, hasFormValue, interpolateParams, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isDisplayOnlyField, isEqual, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isValueField, isWrapperTypeDefinition, isWrappersBundle, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, toReadonlyFieldTree, withEventFormValue, withLoggerConfig, withPreviousValue, withValidationExecutionDefaults, withValueExclusionDefaults, wrapperProps };
|
|
6999
|
-
export type { ArrayAllowedChildren, ArrayButtonConfig, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, ArrayItemDefinitionTemplate, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerAllowedChildren, ContainerField, ContainerFieldTypes, CssWrapper, CustomFnConfig, CustomFunction, CustomValidator, CustomValidatorConfig, DeclarativeHttpValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldScope, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTypeDefinition, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, InferFormValue$1 as InferFormValue, InferWrapperRegistry, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, PageAllowedChildren, PageField, Prettify, ReadonlyFieldTree, RegisteredFieldTypes, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationExecutionConfig, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals, WrapperConfig, WrapperFieldInputs, WrapperRegistration, WrapperTypeDefinition, WrappersBundle };
|
|
8138
|
+
export { ADDON_ACTION_HANDLERS, ADDON_ACTION_REGISTRY, ADDON_KIND_COMPONENT_CACHE, ADDON_KIND_DEFINITIONS, ADDON_KIND_REGISTRY, ARRAY_CONTEXT, AppendArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ComponentAddonComponent, ConsoleLogger, ContainerFieldComponent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DF_FIELD_TEMPLATES, DfAddonSlot, DfTemplate, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, EventDispatcher, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GROUP_CONTEXT, GroupFieldComponent, INITIALIZATION_TIMEOUT_MS, InsertArrayItemEvent, MoveArrayItemEvent, NextPageEvent, NoopLogger, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, RootFormRegistryService, ShiftArrayItemEvent, SubmitEvent, TemplateAddonComponent, TextAddonComponent, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayEvent, arrayFieldMapper, baseFieldMapper, buildBaseInputs, containerFieldMapper, createField, createWrappers, dynamicTextToObservable, evaluateNonFieldDisabled, evaluateNonFieldHidden, field, formConfig, formatAddonWarning, getArrayLength, groupFieldMapper, hasFormValue, injectAddonKindRegistry, injectFieldSignalContext, injectFieldsSupportingAddons, interpolateParams, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isValueField, isWrapperTypeDefinition, isWrappersBundle, logAddonWarnings, omit, pageFieldMapper, provideAddonActions, provideDynamicForm, resolveDynamicValue, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, runPresetAction, sanitizeFormConfig, sanitizeFormConfigPure, textFieldMapper, toReadonlyFieldTree, validateFieldAddons, walkAndValidateAddons, withCustomAddon, withEventFormValue, withLegacyStatusClasses, withLoggerConfig, withPreviousValue, withValidationExecutionDefaults, withValueExclusionDefaults, wrapperProps, writeToFieldValue };
|
|
8139
|
+
export type { AddonActionContext, AddonActionHandler, AddonActionPreset, AddonActionsFeature, AddonKindDefinition, AddonKindSchema, AddonShapeValidator, AddonSlot, AddonWarning, AnyAddon, ArrayAllowedChildren, ArrayButtonConfig, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, ArrayItemDefinitionTemplate, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseAddon, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, CommonAddonActionPreset, CommonAddonSlot, ComponentAddon, ConditionalExpression, ContainerAllowedChildren, ContainerField, ContainerFieldTypes, CssWrapper, CustomFnConfig, CustomFunction, CustomValidator, CustomValidatorConfig, DeclarativeHttpValidatorConfig, DynamicFormActionRegistry, DynamicFormAddonActionPresetRegistry, DynamicFormAddonRegistry, DynamicFormAddonSlotRegistry, DynamicFormFieldRegistry, DynamicText, DynamicValue, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldAddonSupport, FieldAddonSupportEntry, FieldBoundAddonActionContext, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldScope, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTypeDefinition, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, InferFormValue$1 as InferFormValue, InferWrapperRegistry, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, OrphanAddonActionContext, PageAllowedChildren, PageField, PresetCollaborators, Prettify, ReadonlyFieldTree, RegisteredActionRef, RegisteredFieldTypes, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SanitizeFormConfigOptions, SanitizedFormConfig, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TemplateAddon, TextAddon, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationExecutionConfig, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals, WrapperConfig, WrapperFieldInputs, WrapperRegistration, WrapperTypeDefinition, WrappersBundle };
|