@ng-forge/dynamic-forms 0.9.0-next.11 → 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-DJ-LiLm_.mjs → ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs} +158 -73
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-Ckpe0rW7.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-Pcxg5PQu.mjs → ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs} +12 -8
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-BD7Lpcts.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-a4RstR2Z.mjs → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-a4RstR2Z.mjs.map → ng-forge-dynamic-forms-container-utils-CeHyhb2v.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-CJACCIEK.mjs → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs} +5 -5
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-CJACCIEK.mjs.map → ng-forge-dynamic-forms-css-wrapper.component-DkyuJH6T.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-Bjf_I0D_.mjs → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-Bjf_I0D_.mjs.map → ng-forge-dynamic-forms-emit-initialization-u4H5jr35.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-fzKwPrQr.mjs → ng-forge-dynamic-forms-group-field.component-C9xUHrXp.mjs} +15 -11
- 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-o3rJX4vG.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BbgkzKuI.mjs} +1941 -407
- 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-BwNIx3R1.mjs.map +1 -0
- 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-B9SK8Vun.mjs → ng-forge-dynamic-forms-text-field.component-CpgtswdO.mjs} +5 -5
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-B9SK8Vun.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 +2320 -1160
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-DJ-LiLm_.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-Pcxg5PQu.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-fzKwPrQr.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-o3rJX4vG.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BX4R9GGa.mjs +0 -110
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BX4R9GGa.mjs.map +0 -1
- 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
|
};
|
|
@@ -16,8 +158,6 @@ type Prettify<T> = {
|
|
|
16
158
|
[K in keyof T]: T[K];
|
|
17
159
|
} & {};
|
|
18
160
|
|
|
19
|
-
type DynamicText = string | Observable<string> | Signal<string>;
|
|
20
|
-
|
|
21
161
|
/**
|
|
22
162
|
* Interface for dynamic forms logger implementations.
|
|
23
163
|
* Allows custom logging integrations (Sentry, DataDog, etc.)
|
|
@@ -589,351 +729,116 @@ interface ContainerLogicConfig {
|
|
|
589
729
|
}
|
|
590
730
|
|
|
591
731
|
/**
|
|
592
|
-
*
|
|
593
|
-
*
|
|
732
|
+
* Row field interface for creating horizontal layouts.
|
|
733
|
+
* A row is a synthetic field type that resolves to a Container at runtime,
|
|
734
|
+
* with a synthesized `{ type: 'row' }` wrapper applied for layout. The row
|
|
735
|
+
* itself does not hold a value — its children flatten into the parent form.
|
|
736
|
+
*
|
|
737
|
+
* Note: Rows do not support `meta` since they have no native form element.
|
|
594
738
|
*/
|
|
595
|
-
|
|
739
|
+
interface RowField<TFields extends readonly RowAllowedChildren[] = readonly RowAllowedChildren[]> extends FieldDef<never> {
|
|
740
|
+
type: 'row';
|
|
741
|
+
/** Child definitions to render within this row */
|
|
742
|
+
readonly fields: TFields;
|
|
743
|
+
/** Row fields do not have a label property **/
|
|
744
|
+
readonly label?: never;
|
|
745
|
+
/** Rows do not support meta - they have no native form element **/
|
|
746
|
+
readonly meta?: never;
|
|
747
|
+
/**
|
|
748
|
+
* Logic configurations for conditional row visibility.
|
|
749
|
+
* Only 'hidden' type logic is supported for rows.
|
|
750
|
+
*/
|
|
751
|
+
readonly logic?: ContainerLogicConfig[];
|
|
752
|
+
}
|
|
596
753
|
/**
|
|
597
|
-
*
|
|
598
|
-
* - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
|
|
599
|
-
* - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
|
|
600
|
-
*
|
|
601
|
-
* This allows support for:
|
|
602
|
-
* - Primitive arrays: `['tag1', 'tag2']`
|
|
603
|
-
* - Object arrays: `[{ name: 'Alice', email: '...' }]`
|
|
604
|
-
* - Heterogeneous arrays: `[{ value: 'x' }, 'y']` (mixed primitives and objects)
|
|
754
|
+
* Type guard for RowField with proper type narrowing
|
|
605
755
|
*/
|
|
606
|
-
|
|
756
|
+
declare function isRowField(field: FieldDef<any>): field is RowField;
|
|
757
|
+
|
|
607
758
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
* - The outer `fields` array defines INITIAL ITEMS (each element is one array item to render)
|
|
612
|
-
* - Each element can be either:
|
|
613
|
-
* - A single FieldDef (primitive item) - the field's value is extracted directly
|
|
614
|
-
* - An array of FieldDefs (object item) - fields are merged into an object
|
|
615
|
-
* - Items can have different field configurations (heterogeneous arrays supported)
|
|
616
|
-
* - Empty `fields: []` means no initial items - user adds via buttons with explicit templates
|
|
617
|
-
* - Initial values are declared via `value` property on each field definition
|
|
618
|
-
*
|
|
619
|
-
* @example
|
|
620
|
-
* // Primitive array: ['angular', 'typescript']
|
|
621
|
-
* {
|
|
622
|
-
* key: 'tags',
|
|
623
|
-
* type: 'array',
|
|
624
|
-
* fields: [
|
|
625
|
-
* { key: 'tag', type: 'input', value: 'angular' }, // Single field = primitive
|
|
626
|
-
* { key: 'tag', type: 'input', value: 'typescript' },
|
|
627
|
-
* ]
|
|
628
|
-
* }
|
|
629
|
-
*
|
|
630
|
-
* @example
|
|
631
|
-
* // Object array: [{ name: 'Alice', email: '...' }]
|
|
632
|
-
* {
|
|
633
|
-
* key: 'contacts',
|
|
634
|
-
* type: 'array',
|
|
635
|
-
* fields: [
|
|
636
|
-
* [ // Array = object
|
|
637
|
-
* { key: 'name', type: 'input', value: 'Alice' },
|
|
638
|
-
* { key: 'email', type: 'input', value: 'alice@example.com' }
|
|
639
|
-
* ],
|
|
640
|
-
* ]
|
|
641
|
-
* }
|
|
642
|
-
*
|
|
643
|
-
* @example
|
|
644
|
-
* // Heterogeneous array: [{ label: 'Structured' }, 'Simple']
|
|
645
|
-
* {
|
|
646
|
-
* key: 'items',
|
|
647
|
-
* type: 'array',
|
|
648
|
-
* fields: [
|
|
649
|
-
* [{ key: 'label', type: 'input', value: 'Structured' }], // Object item
|
|
650
|
-
* { key: 'value', type: 'input', value: 'Simple' }, // Primitive item
|
|
651
|
-
* ]
|
|
652
|
-
* }
|
|
653
|
-
*
|
|
654
|
-
* @example
|
|
655
|
-
* // Empty array (add items via buttons)
|
|
656
|
-
* {
|
|
657
|
-
* key: 'tags',
|
|
658
|
-
* type: 'array',
|
|
659
|
-
* fields: [] // No initial items - user adds via button with template
|
|
660
|
-
* }
|
|
759
|
+
* Group field interface for creating logical field groupings that map to object values
|
|
760
|
+
* Groups create nested form structures where child field values are collected into an object
|
|
761
|
+
* This is a programmatic grouping only - users cannot customize this field type
|
|
661
762
|
*
|
|
662
|
-
*
|
|
663
|
-
* but NOT pages or other
|
|
763
|
+
* TypeScript cannot enforce field nesting rules due to circular dependency limitations.
|
|
764
|
+
* For documentation: Groups should contain rows and leaf fields, but NOT pages or other groups.
|
|
765
|
+
* Runtime validation enforces these rules.
|
|
664
766
|
*
|
|
665
|
-
* Note:
|
|
767
|
+
* Note: Groups are container fields and do not support `meta` since they have no native form element.
|
|
666
768
|
*/
|
|
667
|
-
interface
|
|
668
|
-
type: '
|
|
669
|
-
/**
|
|
670
|
-
* Array of item definitions. Each element defines one array item:
|
|
671
|
-
* - Single FieldDef = primitive item (field's value is extracted directly)
|
|
672
|
-
* - Array of FieldDefs = object item (fields are merged into an object)
|
|
673
|
-
* - Empty array `[]` = no initial items (add via buttons with explicit templates)
|
|
674
|
-
* - Items can have different configurations (heterogeneous arrays supported)
|
|
675
|
-
* - Initial values are set via `value` property on each field definition
|
|
676
|
-
*/
|
|
769
|
+
interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never> {
|
|
770
|
+
type: 'group';
|
|
677
771
|
readonly fields: TFields;
|
|
678
|
-
/**
|
|
772
|
+
/** Groups do not have a label property - they are logical containers only **/
|
|
679
773
|
readonly label?: never;
|
|
680
|
-
/**
|
|
774
|
+
/** Groups do not support meta - they have no native form element **/
|
|
681
775
|
readonly meta?: never;
|
|
682
776
|
/**
|
|
683
|
-
* Logic configurations for conditional
|
|
684
|
-
* Only 'hidden' type logic is supported for
|
|
777
|
+
* Logic configurations for conditional group visibility.
|
|
778
|
+
* Only 'hidden' type logic is supported for groups.
|
|
685
779
|
*/
|
|
686
780
|
readonly logic?: ContainerLogicConfig[];
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Type guard for GroupField with proper type narrowing
|
|
784
|
+
*/
|
|
785
|
+
declare function isGroupField(field: FieldDef<any>): field is GroupField;
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Page field interface for creating top-level page layouts
|
|
789
|
+
* This is a special field type that contains other field definitions
|
|
790
|
+
* The page itself doesn't have a value - it's a layout container like row
|
|
791
|
+
* Pages can only be used at the top level and cannot be nested
|
|
792
|
+
* This is a programmatic field type only - users cannot customize this field type
|
|
793
|
+
*
|
|
794
|
+
* TypeScript cannot enforce field nesting rules due to circular dependency limitations.
|
|
795
|
+
* For documentation: Pages should contain rows, groups, and leaf fields, but NOT other pages.
|
|
796
|
+
* Runtime validation enforces these rules.
|
|
797
|
+
*
|
|
798
|
+
* Note: Pages are container fields and do not support `meta` since they have no native form element.
|
|
799
|
+
*/
|
|
800
|
+
interface PageField<TFields extends readonly PageAllowedChildren[] = PageAllowedChildren[]> extends FieldDef<never> {
|
|
801
|
+
type: 'page';
|
|
802
|
+
/** Child field definitions to render within this page */
|
|
803
|
+
readonly fields: TFields;
|
|
804
|
+
/** Page fields do not have a label property **/
|
|
805
|
+
readonly label?: never;
|
|
806
|
+
/** Pages do not support meta - they have no native form element **/
|
|
807
|
+
readonly meta?: never;
|
|
687
808
|
/**
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
*
|
|
694
|
-
*
|
|
809
|
+
* Logic configurations for conditional page visibility.
|
|
810
|
+
* Only 'hidden' type logic is supported for pages.
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* ```typescript
|
|
814
|
+
* {
|
|
815
|
+
* key: 'businessPage',
|
|
816
|
+
* type: 'page',
|
|
817
|
+
* logic: [{
|
|
818
|
+
* type: 'hidden',
|
|
819
|
+
* condition: {
|
|
820
|
+
* type: 'fieldValue',
|
|
821
|
+
* fieldPath: 'accountType',
|
|
822
|
+
* operator: 'notEquals',
|
|
823
|
+
* value: 'business',
|
|
824
|
+
* },
|
|
825
|
+
* }],
|
|
826
|
+
* fields: [...]
|
|
827
|
+
* }
|
|
828
|
+
* ```
|
|
695
829
|
*/
|
|
696
|
-
readonly
|
|
830
|
+
readonly logic?: ContainerLogicConfig[];
|
|
697
831
|
}
|
|
698
832
|
/**
|
|
699
|
-
* Type guard for
|
|
700
|
-
* Validates that the field is an array type with a fields property that is an array.
|
|
701
|
-
* Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
|
|
833
|
+
* Type guard for PageField with proper type narrowing
|
|
702
834
|
*/
|
|
703
|
-
declare function
|
|
835
|
+
declare function isPageField(field: FieldDef<unknown>): field is PageField;
|
|
836
|
+
|
|
704
837
|
/**
|
|
705
|
-
*
|
|
838
|
+
* Helper type to convert union to intersection.
|
|
839
|
+
* Uses `U extends U` to trigger distributive conditional type behavior.
|
|
706
840
|
*/
|
|
707
|
-
|
|
708
|
-
/** Custom label for the button */
|
|
709
|
-
readonly label?: string;
|
|
710
|
-
/** Additional properties passed to the button component */
|
|
711
|
-
readonly props?: Record<string, unknown>;
|
|
712
|
-
}
|
|
713
|
-
/**
|
|
714
|
-
* Simplified array field interface for common use cases.
|
|
715
|
-
*
|
|
716
|
-
* Instead of manually specifying each item in `fields`, provide a `template` that defines
|
|
717
|
-
* the structure of a single item, and a `value` array with initial data.
|
|
718
|
-
* The library auto-generates add/remove buttons.
|
|
719
|
-
*
|
|
720
|
-
* Discriminant: `template` presence → simplified API; `Array.isArray(template)` → object vs primitive.
|
|
721
|
-
*
|
|
722
|
-
* @example
|
|
723
|
-
* // Primitive array: ['angular', 'typescript']
|
|
724
|
-
* {
|
|
725
|
-
* key: 'tags',
|
|
726
|
-
* type: 'array',
|
|
727
|
-
* template: { key: 'value', type: 'input', label: 'Tag', required: true },
|
|
728
|
-
* value: ['angular', 'typescript']
|
|
729
|
-
* }
|
|
730
|
-
*
|
|
731
|
-
* @example
|
|
732
|
-
* // Object array: [{ name: 'Jane', phone: '555' }]
|
|
733
|
-
* {
|
|
734
|
-
* key: 'contacts',
|
|
735
|
-
* type: 'array',
|
|
736
|
-
* template: [
|
|
737
|
-
* { key: 'name', type: 'input', label: 'Contact Name', required: true },
|
|
738
|
-
* { key: 'phone', type: 'input', label: 'Phone Number' },
|
|
739
|
-
* ],
|
|
740
|
-
* value: [{ name: 'Jane', phone: '555' }]
|
|
741
|
-
* }
|
|
742
|
-
*
|
|
743
|
-
* @example
|
|
744
|
-
* // Button customization / opt-out
|
|
745
|
-
* {
|
|
746
|
-
* key: 'tags',
|
|
747
|
-
* type: 'array',
|
|
748
|
-
* template: { key: 'value', type: 'input', label: 'Tag' },
|
|
749
|
-
* addButton: { label: 'Add Tag', props: { color: 'primary' } },
|
|
750
|
-
* removeButton: false
|
|
751
|
-
* }
|
|
752
|
-
*/
|
|
753
|
-
interface SimplifiedArrayField extends FieldDef<never> {
|
|
754
|
-
type: 'array';
|
|
755
|
-
/**
|
|
756
|
-
* Template defining the structure of a single array item.
|
|
757
|
-
* - Single field (ArrayAllowedChildren) → primitive array (each item is a single value)
|
|
758
|
-
* - Array of fields (readonly ArrayAllowedChildren[]) → object array (each item is an object)
|
|
759
|
-
*/
|
|
760
|
-
readonly template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
|
|
761
|
-
/** Initial values for the array. Each element creates one array item. */
|
|
762
|
-
readonly value?: readonly unknown[];
|
|
763
|
-
/**
|
|
764
|
-
* Configuration for the auto-generated "Add" button, or `false` to disable it.
|
|
765
|
-
* Defaults to a button with label "Add".
|
|
766
|
-
*/
|
|
767
|
-
readonly addButton?: ArrayButtonConfig | false;
|
|
768
|
-
/**
|
|
769
|
-
* Configuration for the auto-generated "Remove" button on each item, or `false` to disable it.
|
|
770
|
-
* Defaults to a button with label "Remove".
|
|
771
|
-
*/
|
|
772
|
-
readonly removeButton?: ArrayButtonConfig | false;
|
|
773
|
-
/** Simplified arrays do not support the label property */
|
|
774
|
-
readonly label?: never;
|
|
775
|
-
/** Simplified arrays do not support meta */
|
|
776
|
-
readonly meta?: never;
|
|
777
|
-
/**
|
|
778
|
-
* Logic configurations for conditional array visibility.
|
|
779
|
-
* Only 'hidden' type logic is supported for arrays.
|
|
780
|
-
*/
|
|
781
|
-
readonly logic?: ContainerLogicConfig[];
|
|
782
|
-
/**
|
|
783
|
-
* Minimum number of items required in the array.
|
|
784
|
-
* Validation fails if the array has fewer items than this value.
|
|
785
|
-
*/
|
|
786
|
-
readonly minLength?: number;
|
|
787
|
-
/**
|
|
788
|
-
* Maximum number of items allowed in the array.
|
|
789
|
-
* Validation fails if the array has more items than this value.
|
|
790
|
-
*/
|
|
791
|
-
readonly maxLength?: number;
|
|
792
|
-
/** Mutually exclusive with `template` — use `fields` for the full API instead */
|
|
793
|
-
readonly fields?: never;
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Type guard for SimplifiedArrayField.
|
|
797
|
-
* Checks for `type: 'array'` with a `template` property (discriminant from full ArrayField).
|
|
798
|
-
*/
|
|
799
|
-
declare function isSimplifiedArrayField(field: FieldDef<any>): field is SimplifiedArrayField;
|
|
800
|
-
|
|
801
|
-
/**
|
|
802
|
-
* Group field interface for creating logical field groupings that map to object values
|
|
803
|
-
* Groups create nested form structures where child field values are collected into an object
|
|
804
|
-
* This is a programmatic grouping only - users cannot customize this field type
|
|
805
|
-
*
|
|
806
|
-
* TypeScript cannot enforce field nesting rules due to circular dependency limitations.
|
|
807
|
-
* For documentation: Groups should contain rows and leaf fields, but NOT pages or other groups.
|
|
808
|
-
* Runtime validation enforces these rules.
|
|
809
|
-
*
|
|
810
|
-
* Note: Groups are container fields and do not support `meta` since they have no native form element.
|
|
811
|
-
*/
|
|
812
|
-
interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never> {
|
|
813
|
-
type: 'group';
|
|
814
|
-
readonly fields: TFields;
|
|
815
|
-
/** Groups do not have a label property - they are logical containers only **/
|
|
816
|
-
readonly label?: never;
|
|
817
|
-
/** Groups do not support meta - they have no native form element **/
|
|
818
|
-
readonly meta?: never;
|
|
819
|
-
/**
|
|
820
|
-
* Logic configurations for conditional group visibility.
|
|
821
|
-
* Only 'hidden' type logic is supported for groups.
|
|
822
|
-
*/
|
|
823
|
-
readonly logic?: ContainerLogicConfig[];
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Type guard for GroupField with proper type narrowing
|
|
827
|
-
*/
|
|
828
|
-
declare function isGroupField(field: FieldDef<any>): field is GroupField;
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* Scalar types supported by hidden fields.
|
|
832
|
-
* These are the primitive value types that can be stored.
|
|
833
|
-
*/
|
|
834
|
-
type HiddenScalar = string | number | boolean;
|
|
835
|
-
/**
|
|
836
|
-
* All value types supported by hidden fields.
|
|
837
|
-
* Supports both scalar values and arrays of scalars for storing
|
|
838
|
-
* multiple IDs or similar data.
|
|
839
|
-
*/
|
|
840
|
-
type HiddenValue = HiddenScalar | HiddenScalar[];
|
|
841
|
-
/**
|
|
842
|
-
* Hidden field definition for storing values without rendering UI.
|
|
843
|
-
*
|
|
844
|
-
* Hidden fields participate in the form schema and contribute to form values,
|
|
845
|
-
* but do not render any visible component. They are useful for:
|
|
846
|
-
* - Storing IDs when updating existing records
|
|
847
|
-
* - Persisting metadata that shouldn't be user-editable
|
|
848
|
-
* - Tracking computed values that need to be submitted
|
|
849
|
-
*
|
|
850
|
-
* Unlike the `hidden` property on other fields (which hides a rendered field),
|
|
851
|
-
* this field type renders nothing at all - it's a componentless field.
|
|
852
|
-
*
|
|
853
|
-
* @example
|
|
854
|
-
* ```typescript
|
|
855
|
-
* // Store a record ID for updates
|
|
856
|
-
* const idField: HiddenField<string> = {
|
|
857
|
-
* type: 'hidden',
|
|
858
|
-
* key: 'id',
|
|
859
|
-
* value: '550e8400-e29b-41d4-a716-446655440000',
|
|
860
|
-
* };
|
|
861
|
-
*
|
|
862
|
-
* // Store multiple tag IDs
|
|
863
|
-
* const tagIdsField: HiddenField<number[]> = {
|
|
864
|
-
* type: 'hidden',
|
|
865
|
-
* key: 'tagIds',
|
|
866
|
-
* value: [1, 2, 3],
|
|
867
|
-
* };
|
|
868
|
-
* ```
|
|
869
|
-
*
|
|
870
|
-
* @typeParam TValue - The type of value stored (must be HiddenValue compatible)
|
|
871
|
-
*/
|
|
872
|
-
interface HiddenField<TValue extends HiddenValue = HiddenValue> extends FieldDef<never> {
|
|
873
|
-
/** Discriminant for hidden field type */
|
|
874
|
-
type: 'hidden';
|
|
875
|
-
/**
|
|
876
|
-
* The value to store in the form.
|
|
877
|
-
* This is required - a hidden field without a value serves no purpose.
|
|
878
|
-
*/
|
|
879
|
-
value: TValue;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* Page field interface for creating top-level page layouts
|
|
884
|
-
* This is a special field type that contains other field definitions
|
|
885
|
-
* The page itself doesn't have a value - it's a layout container like row
|
|
886
|
-
* Pages can only be used at the top level and cannot be nested
|
|
887
|
-
* This is a programmatic field type only - users cannot customize this field type
|
|
888
|
-
*
|
|
889
|
-
* TypeScript cannot enforce field nesting rules due to circular dependency limitations.
|
|
890
|
-
* For documentation: Pages should contain rows, groups, and leaf fields, but NOT other pages.
|
|
891
|
-
* Runtime validation enforces these rules.
|
|
892
|
-
*
|
|
893
|
-
* Note: Pages are container fields and do not support `meta` since they have no native form element.
|
|
894
|
-
*/
|
|
895
|
-
interface PageField<TFields extends readonly PageAllowedChildren[] = PageAllowedChildren[]> extends FieldDef<never> {
|
|
896
|
-
type: 'page';
|
|
897
|
-
/** Child field definitions to render within this page */
|
|
898
|
-
readonly fields: TFields;
|
|
899
|
-
/** Page fields do not have a label property **/
|
|
900
|
-
readonly label?: never;
|
|
901
|
-
/** Pages do not support meta - they have no native form element **/
|
|
902
|
-
readonly meta?: never;
|
|
903
|
-
/**
|
|
904
|
-
* Logic configurations for conditional page visibility.
|
|
905
|
-
* Only 'hidden' type logic is supported for pages.
|
|
906
|
-
*
|
|
907
|
-
* @example
|
|
908
|
-
* ```typescript
|
|
909
|
-
* {
|
|
910
|
-
* key: 'businessPage',
|
|
911
|
-
* type: 'page',
|
|
912
|
-
* logic: [{
|
|
913
|
-
* type: 'hidden',
|
|
914
|
-
* condition: {
|
|
915
|
-
* type: 'fieldValue',
|
|
916
|
-
* fieldPath: 'accountType',
|
|
917
|
-
* operator: 'notEquals',
|
|
918
|
-
* value: 'business',
|
|
919
|
-
* },
|
|
920
|
-
* }],
|
|
921
|
-
* fields: [...]
|
|
922
|
-
* }
|
|
923
|
-
* ```
|
|
924
|
-
*/
|
|
925
|
-
readonly logic?: ContainerLogicConfig[];
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* Type guard for PageField with proper type narrowing
|
|
929
|
-
*/
|
|
930
|
-
declare function isPageField(field: FieldDef<unknown>): field is PageField;
|
|
931
|
-
|
|
932
|
-
/**
|
|
933
|
-
* Helper type to convert union to intersection.
|
|
934
|
-
* Uses `U extends U` to trigger distributive conditional type behavior.
|
|
935
|
-
*/
|
|
936
|
-
type UnionToIntersection<U> = (U extends U ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
841
|
+
type UnionToIntersection<U> = (U extends U ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
937
842
|
/**
|
|
938
843
|
* Depth counter for recursion limiting (prevents infinite type instantiation)
|
|
939
844
|
*/
|
|
@@ -2420,7 +2325,6 @@ type SubmissionActionResult = Promise<TreeValidationResult> | Observable<TreeVal
|
|
|
2420
2325
|
* @typeParam TValue - The form value type
|
|
2421
2326
|
*
|
|
2422
2327
|
* @public
|
|
2423
|
-
* @experimental
|
|
2424
2328
|
*/
|
|
2425
2329
|
interface SubmissionConfig<TValue = unknown> {
|
|
2426
2330
|
/**
|
|
@@ -3434,18 +3338,70 @@ interface TextField extends FieldDef<TextProps> {
|
|
|
3434
3338
|
}
|
|
3435
3339
|
|
|
3436
3340
|
/**
|
|
3437
|
-
*
|
|
3341
|
+
* Scalar types supported by hidden fields.
|
|
3342
|
+
* These are the primitive value types that can be stored.
|
|
3343
|
+
*/
|
|
3344
|
+
type HiddenScalar = string | number | boolean;
|
|
3345
|
+
/**
|
|
3346
|
+
* All value types supported by hidden fields.
|
|
3347
|
+
* Supports both scalar values and arrays of scalars for storing
|
|
3348
|
+
* multiple IDs or similar data.
|
|
3349
|
+
*/
|
|
3350
|
+
type HiddenValue = HiddenScalar | HiddenScalar[];
|
|
3351
|
+
/**
|
|
3352
|
+
* Hidden field definition for storing values without rendering UI.
|
|
3438
3353
|
*
|
|
3439
|
-
*
|
|
3440
|
-
*
|
|
3354
|
+
* Hidden fields participate in the form schema and contribute to form values,
|
|
3355
|
+
* but do not render any visible component. They are useful for:
|
|
3356
|
+
* - Storing IDs when updating existing records
|
|
3357
|
+
* - Persisting metadata that shouldn't be user-editable
|
|
3358
|
+
* - Tracking computed values that need to be submitted
|
|
3359
|
+
*
|
|
3360
|
+
* Unlike the `hidden` property on other fields (which hides a rendered field),
|
|
3361
|
+
* this field type renders nothing at all - it's a componentless field.
|
|
3441
3362
|
*
|
|
3442
3363
|
* @example
|
|
3443
3364
|
* ```typescript
|
|
3444
|
-
*
|
|
3445
|
-
*
|
|
3446
|
-
*
|
|
3365
|
+
* // Store a record ID for updates
|
|
3366
|
+
* const idField: HiddenField<string> = {
|
|
3367
|
+
* type: 'hidden',
|
|
3368
|
+
* key: 'id',
|
|
3369
|
+
* value: '550e8400-e29b-41d4-a716-446655440000',
|
|
3447
3370
|
* };
|
|
3448
|
-
*
|
|
3371
|
+
*
|
|
3372
|
+
* // Store multiple tag IDs
|
|
3373
|
+
* const tagIdsField: HiddenField<number[]> = {
|
|
3374
|
+
* type: 'hidden',
|
|
3375
|
+
* key: 'tagIds',
|
|
3376
|
+
* value: [1, 2, 3],
|
|
3377
|
+
* };
|
|
3378
|
+
* ```
|
|
3379
|
+
*
|
|
3380
|
+
* @typeParam TValue - The type of value stored (must be HiddenValue compatible)
|
|
3381
|
+
*/
|
|
3382
|
+
interface HiddenField<TValue extends HiddenValue = HiddenValue> extends FieldDef<never> {
|
|
3383
|
+
/** Discriminant for hidden field type */
|
|
3384
|
+
type: 'hidden';
|
|
3385
|
+
/**
|
|
3386
|
+
* The value to store in the form.
|
|
3387
|
+
* This is required - a hidden field without a value serves no purpose.
|
|
3388
|
+
*/
|
|
3389
|
+
value: TValue;
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
/**
|
|
3393
|
+
* Configuration for the built-in 'css' wrapper type.
|
|
3394
|
+
*
|
|
3395
|
+
* The CSS wrapper applies CSS classes around wrapped content via a DynamicText
|
|
3396
|
+
* input that resolves to space-separated class names.
|
|
3397
|
+
*
|
|
3398
|
+
* @example
|
|
3399
|
+
* ```typescript
|
|
3400
|
+
* const wrapper: CssWrapper = {
|
|
3401
|
+
* type: 'css',
|
|
3402
|
+
* cssClasses: 'my-css-class another-class',
|
|
3403
|
+
* };
|
|
3404
|
+
* ```
|
|
3449
3405
|
*/
|
|
3450
3406
|
interface CssWrapper {
|
|
3451
3407
|
readonly type: 'css';
|
|
@@ -3472,6 +3428,118 @@ interface RowWrapper {
|
|
|
3472
3428
|
readonly type: 'row';
|
|
3473
3429
|
}
|
|
3474
3430
|
|
|
3431
|
+
/**
|
|
3432
|
+
* Resolves a wrapper type name to its registered config interface.
|
|
3433
|
+
*
|
|
3434
|
+
* When `TWrappers` is a specific registered key (e.g., `'css'`), resolves to
|
|
3435
|
+
* the full config type from `FieldRegistryWrappers` (e.g., `CssWrapper`),
|
|
3436
|
+
* providing type-safe access to wrapper-specific properties like `cssClasses`.
|
|
3437
|
+
*
|
|
3438
|
+
* When `TWrappers` is the full `RegisteredWrapperTypes` union, distributes
|
|
3439
|
+
* to produce a discriminated union of all registered wrapper configs.
|
|
3440
|
+
*
|
|
3441
|
+
* @example
|
|
3442
|
+
* ```typescript
|
|
3443
|
+
* // Resolves to CssWrapper — cssClasses is typed
|
|
3444
|
+
* type CssConfig = WrapperConfig<'css'>;
|
|
3445
|
+
*
|
|
3446
|
+
* // Union of all registered wrapper configs
|
|
3447
|
+
* type AnyConfig = WrapperConfig;
|
|
3448
|
+
* ```
|
|
3449
|
+
*/
|
|
3450
|
+
type WrapperConfig<TWrappers extends RegisteredWrapperTypes = RegisteredWrapperTypes> = TWrappers extends keyof FieldRegistryWrappers ? FieldRegistryWrappers[TWrappers] : {
|
|
3451
|
+
readonly type: TWrappers;
|
|
3452
|
+
};
|
|
3453
|
+
/**
|
|
3454
|
+
* Signature of a lazy component loader — either a direct component class or
|
|
3455
|
+
* an ES module whose `default` export is the component. Shared by field and
|
|
3456
|
+
* wrapper registrations so the `loadComponent: () => import('./x.component')`
|
|
3457
|
+
* idiom types cleanly in both places.
|
|
3458
|
+
*/
|
|
3459
|
+
type LazyComponentLoader<T = unknown> = () => Promise<Type<T> | {
|
|
3460
|
+
default: Type<T>;
|
|
3461
|
+
}>;
|
|
3462
|
+
/**
|
|
3463
|
+
* Configuration interface for registering wrapper types.
|
|
3464
|
+
*
|
|
3465
|
+
* Defines how a wrapper component is loaded and identified. Wrapper components
|
|
3466
|
+
* provide visual decoration around field content (sections, headers, styling)
|
|
3467
|
+
* without affecting the form data structure.
|
|
3468
|
+
*
|
|
3469
|
+
* @example
|
|
3470
|
+
* ```typescript
|
|
3471
|
+
* const SectionWrapper: WrapperTypeDefinition = {
|
|
3472
|
+
* name: 'section',
|
|
3473
|
+
* loadComponent: () => import('./section-wrapper.component').then(m => m.SectionWrapperComponent),
|
|
3474
|
+
* };
|
|
3475
|
+
* ```
|
|
3476
|
+
*/
|
|
3477
|
+
interface WrapperTypeDefinition<T extends WrapperConfig = WrapperConfig> {
|
|
3478
|
+
/** Unique identifier for the wrapper type (also serves as discriminant from FieldTypeDefinition) */
|
|
3479
|
+
wrapperName: string;
|
|
3480
|
+
/** Wrapper definition type marker (internal use) */
|
|
3481
|
+
_wrapper?: T;
|
|
3482
|
+
/**
|
|
3483
|
+
* Function to load the wrapper component (supports lazy loading).
|
|
3484
|
+
* Returns a Promise that resolves to the component class or module with default export.
|
|
3485
|
+
*/
|
|
3486
|
+
loadComponent: LazyComponentLoader;
|
|
3487
|
+
/**
|
|
3488
|
+
* Field types this wrapper should auto-apply to.
|
|
3489
|
+
*
|
|
3490
|
+
* When a field's `type` matches any entry, the wrapper is injected into that
|
|
3491
|
+
* field's effective wrapper chain at the lowest priority (can be overridden
|
|
3492
|
+
* by `FormConfig.defaultWrappers` or the field-level `wrappers` array, and
|
|
3493
|
+
* fully cleared with `wrappers: null`).
|
|
3494
|
+
*/
|
|
3495
|
+
types?: readonly string[];
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Type guard for WrapperTypeDefinition.
|
|
3499
|
+
*
|
|
3500
|
+
* Discriminates via `wrapperName` — field types use `name`, wrapper types use `wrapperName`.
|
|
3501
|
+
*/
|
|
3502
|
+
declare function isWrapperTypeDefinition(value: unknown): value is WrapperTypeDefinition;
|
|
3503
|
+
/**
|
|
3504
|
+
* Contract that wrapper components must satisfy.
|
|
3505
|
+
*
|
|
3506
|
+
* Each wrapper exposes a `#fieldComponent` ViewContainerRef where the inner
|
|
3507
|
+
* content (next wrapper, or the field) is rendered imperatively. Config
|
|
3508
|
+
* properties (minus `type`) are set on the component via `setInput`; a
|
|
3509
|
+
* wrapper opts into a key by declaring a matching `input()`, and unknown
|
|
3510
|
+
* keys are silently dropped. Wrappers may additionally declare a
|
|
3511
|
+
* `fieldInputs` input for the wrapped field's mapper outputs (see
|
|
3512
|
+
* `WrapperFieldInputs` for when that's undefined).
|
|
3513
|
+
*
|
|
3514
|
+
* @example
|
|
3515
|
+
* ```typescript
|
|
3516
|
+
* @Component({
|
|
3517
|
+
* template: `
|
|
3518
|
+
* <dbx-section [header]="header() ?? ''">
|
|
3519
|
+
* <ng-container #fieldComponent></ng-container>
|
|
3520
|
+
* </dbx-section>
|
|
3521
|
+
* `,
|
|
3522
|
+
* })
|
|
3523
|
+
* export class SectionWrapperComponent implements FieldWrapper {
|
|
3524
|
+
* readonly fieldComponent = viewChild.required('fieldComponent', { read: ViewContainerRef });
|
|
3525
|
+
* readonly header = input<string>();
|
|
3526
|
+
* readonly fieldInputs = input<WrapperFieldInputs>();
|
|
3527
|
+
* }
|
|
3528
|
+
* ```
|
|
3529
|
+
*/
|
|
3530
|
+
interface FieldWrapper {
|
|
3531
|
+
/** ViewContainerRef slot where inner content is rendered */
|
|
3532
|
+
readonly fieldComponent: Signal<ViewContainerRef>;
|
|
3533
|
+
}
|
|
3534
|
+
/**
|
|
3535
|
+
* Injection token for the wrapper type registry.
|
|
3536
|
+
*
|
|
3537
|
+
* Provides access to the map of registered wrapper types. The registry is
|
|
3538
|
+
* populated via `provideDynamicForm()` and used by `ContainerFieldComponent` to
|
|
3539
|
+
* resolve wrapper types to their component implementations.
|
|
3540
|
+
*/
|
|
3541
|
+
declare const WRAPPER_REGISTRY: InjectionToken<Map<string, WrapperTypeDefinition<RowWrapper | _ng_forge_dynamic_forms.CssWrapper>>>;
|
|
3542
|
+
|
|
3475
3543
|
/**
|
|
3476
3544
|
* Container field interface for wrapping child fields with UI chrome.
|
|
3477
3545
|
*
|
|
@@ -3554,326 +3622,397 @@ interface ContainerField<TFields extends readonly ContainerAllowedChildren[] = r
|
|
|
3554
3622
|
declare function isContainerTypedField(field: FieldDef<any>): field is ContainerField;
|
|
3555
3623
|
|
|
3556
3624
|
/**
|
|
3557
|
-
*
|
|
3625
|
+
* Type constraints for field nesting rules
|
|
3626
|
+
* These ensure that container fields can only contain valid child field types
|
|
3558
3627
|
*
|
|
3559
|
-
*
|
|
3560
|
-
* ```typescript
|
|
3561
|
-
* declare module '@ng-forge/dynamic-forms' {
|
|
3562
|
-
* interface FieldRegistryContainers {
|
|
3563
|
-
* 'my-container': MyContainerFieldDef;
|
|
3564
|
-
* }
|
|
3565
|
-
* }
|
|
3566
|
-
* ```
|
|
3628
|
+
* Note: We explicitly list types instead of using Exclude to avoid circular dependencies
|
|
3567
3629
|
*/
|
|
3568
|
-
interface FieldRegistryContainers {
|
|
3569
|
-
page: PageField;
|
|
3570
|
-
row: RowField;
|
|
3571
|
-
group: GroupField;
|
|
3572
|
-
array: ArrayField | SimplifiedArrayField;
|
|
3573
|
-
container: ContainerField;
|
|
3574
|
-
}
|
|
3575
3630
|
/**
|
|
3576
|
-
*
|
|
3577
|
-
*
|
|
3578
|
-
* @example
|
|
3579
|
-
* ```typescript
|
|
3580
|
-
* declare module '@ng-forge/dynamic-forms' {
|
|
3581
|
-
* interface FieldRegistryLeaves {
|
|
3582
|
-
* 'my-input': MyInputFieldDef;
|
|
3583
|
-
* }
|
|
3584
|
-
* }
|
|
3585
|
-
* ```
|
|
3631
|
+
* Fields that are allowed as children of Page fields
|
|
3632
|
+
* Pages can contain: rows, groups, arrays, and leaf fields (but NOT other pages)
|
|
3586
3633
|
*/
|
|
3587
|
-
|
|
3588
|
-
text: TextField;
|
|
3589
|
-
hidden: HiddenField;
|
|
3590
|
-
}
|
|
3634
|
+
type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
3591
3635
|
/**
|
|
3592
|
-
*
|
|
3593
|
-
*
|
|
3594
|
-
* Augment this interface to add type-safe wrapper type definitions.
|
|
3595
|
-
*
|
|
3596
|
-
* @example
|
|
3597
|
-
* ```typescript
|
|
3598
|
-
* declare module '@ng-forge/dynamic-forms' {
|
|
3599
|
-
* interface FieldRegistryWrappers {
|
|
3600
|
-
* 'section': MySectionFieldDef
|
|
3601
|
-
* }
|
|
3602
|
-
* }
|
|
3603
|
-
* ```
|
|
3636
|
+
* Fields that are allowed as children of Group fields
|
|
3637
|
+
* Groups can contain: rows and leaf fields (but NOT pages or other groups)
|
|
3604
3638
|
*/
|
|
3605
|
-
|
|
3606
|
-
css: CssWrapper;
|
|
3607
|
-
row: RowWrapper;
|
|
3608
|
-
}
|
|
3639
|
+
type GroupAllowedChildren = LeafFieldTypes | RowField | ContainerField;
|
|
3609
3640
|
/**
|
|
3610
|
-
*
|
|
3611
|
-
*
|
|
3612
|
-
*
|
|
3613
|
-
* Container fields: Layout fields that contain other fields (page, row, group, array)
|
|
3614
|
-
* Leaf fields: Fields that can hold values or display content (input, text, etc.)
|
|
3615
|
-
*
|
|
3616
|
-
* To add custom fields, augment FieldRegistryContainers or FieldRegistryLeaves
|
|
3641
|
+
* Fields that are allowed as children of Array fields
|
|
3642
|
+
* Arrays can contain: rows, groups, and leaf fields (but NOT pages or other arrays)
|
|
3643
|
+
* Groups are used for creating object arrays where each array item is an object
|
|
3617
3644
|
*/
|
|
3618
|
-
|
|
3619
|
-
/**
|
|
3620
|
-
* Container fields that hold other fields (no value, have children)
|
|
3621
|
-
*/
|
|
3622
|
-
containers: FieldRegistryContainers;
|
|
3623
|
-
/**
|
|
3624
|
-
* Leaf fields that have values or display content
|
|
3625
|
-
*/
|
|
3626
|
-
leaves: FieldRegistryLeaves;
|
|
3627
|
-
/**
|
|
3628
|
-
* Field wrappers types
|
|
3629
|
-
*/
|
|
3630
|
-
wrappers: FieldRegistryWrappers;
|
|
3631
|
-
}
|
|
3645
|
+
type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField | ContainerField;
|
|
3632
3646
|
/**
|
|
3633
|
-
*
|
|
3647
|
+
* Fields that are allowed as children of Container fields.
|
|
3648
|
+
* Containers are pure layout primitives that flatten their children into the
|
|
3649
|
+
* parent form, so most registered field types may appear inside — including
|
|
3650
|
+
* hidden fields and other containers — but NOT pages.
|
|
3634
3651
|
*/
|
|
3635
|
-
type
|
|
3652
|
+
type ContainerAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
3636
3653
|
/**
|
|
3637
|
-
*
|
|
3654
|
+
* Row is a synthetic field type that resolves to a Container at runtime, so
|
|
3655
|
+
* it accepts the same children as a Container.
|
|
3638
3656
|
*/
|
|
3639
|
-
type
|
|
3657
|
+
type RowAllowedChildren = ContainerAllowedChildren;
|
|
3658
|
+
|
|
3640
3659
|
/**
|
|
3641
|
-
*
|
|
3660
|
+
* An array item template is an array of allowed children that defines one OBJECT array item's fields.
|
|
3661
|
+
* Used for object arrays where each item is `{ key1: value1, key2: value2, ... }`.
|
|
3642
3662
|
*/
|
|
3643
|
-
type
|
|
3663
|
+
type ArrayItemTemplate = readonly ArrayAllowedChildren[];
|
|
3644
3664
|
/**
|
|
3645
|
-
*
|
|
3665
|
+
* An array item definition can be either:
|
|
3666
|
+
* - A single field (ArrayAllowedChildren) for PRIMITIVE array items - extracts field value directly
|
|
3667
|
+
* - An array of fields (ArrayItemTemplate) for OBJECT array items - merges fields into object
|
|
3668
|
+
*
|
|
3669
|
+
* This allows support for:
|
|
3670
|
+
* - Primitive arrays: `['tag1', 'tag2']`
|
|
3671
|
+
* - Object arrays: `[{ name: 'Alice', email: '...' }]`
|
|
3672
|
+
* - Heterogeneous arrays: `[{ value: 'x' }, 'y']` (mixed primitives and objects)
|
|
3646
3673
|
*/
|
|
3647
|
-
type
|
|
3674
|
+
type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate;
|
|
3648
3675
|
/**
|
|
3649
|
-
*
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
*
|
|
3654
|
-
*
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
*
|
|
3659
|
-
* This enables proper type narrowing when defining fields.
|
|
3676
|
+
* Array field interface for creating dynamic field collections that map to array values.
|
|
3677
|
+
*
|
|
3678
|
+
* Key concepts:
|
|
3679
|
+
* - The outer `fields` array defines INITIAL ITEMS (each element is one array item to render)
|
|
3680
|
+
* - Each element can be either:
|
|
3681
|
+
* - A single FieldDef (primitive item) - the field's value is extracted directly
|
|
3682
|
+
* - An array of FieldDefs (object item) - fields are merged into an object
|
|
3683
|
+
* - Items can have different field configurations (heterogeneous arrays supported)
|
|
3684
|
+
* - Empty `fields: []` means no initial items - user adds via buttons with explicit templates
|
|
3685
|
+
* - Initial values are declared via `value` property on each field definition
|
|
3660
3686
|
*
|
|
3661
3687
|
* @example
|
|
3662
|
-
*
|
|
3663
|
-
*
|
|
3664
|
-
*
|
|
3688
|
+
* // Primitive array: ['angular', 'typescript']
|
|
3689
|
+
* {
|
|
3690
|
+
* key: 'tags',
|
|
3691
|
+
* type: 'array',
|
|
3692
|
+
* fields: [
|
|
3693
|
+
* { key: 'tag', type: 'input', value: 'angular' }, // Single field = primitive
|
|
3694
|
+
* { key: 'tag', type: 'input', value: 'typescript' },
|
|
3695
|
+
* ]
|
|
3696
|
+
* }
|
|
3665
3697
|
*
|
|
3666
|
-
*
|
|
3667
|
-
*
|
|
3668
|
-
*
|
|
3669
|
-
* key: '
|
|
3670
|
-
*
|
|
3671
|
-
*
|
|
3672
|
-
*
|
|
3673
|
-
*
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
*
|
|
3678
|
-
* Use this to get proper type inference when working with field unions.
|
|
3698
|
+
* @example
|
|
3699
|
+
* // Object array: [{ name: 'Alice', email: '...' }]
|
|
3700
|
+
* {
|
|
3701
|
+
* key: 'contacts',
|
|
3702
|
+
* type: 'array',
|
|
3703
|
+
* fields: [
|
|
3704
|
+
* [ // Array = object
|
|
3705
|
+
* { key: 'name', type: 'input', value: 'Alice' },
|
|
3706
|
+
* { key: 'email', type: 'input', value: 'alice@example.com' }
|
|
3707
|
+
* ],
|
|
3708
|
+
* ]
|
|
3709
|
+
* }
|
|
3679
3710
|
*
|
|
3680
3711
|
* @example
|
|
3681
|
-
*
|
|
3682
|
-
*
|
|
3683
|
-
*
|
|
3712
|
+
* // Heterogeneous array: [{ label: 'Structured' }, 'Simple']
|
|
3713
|
+
* {
|
|
3714
|
+
* key: 'items',
|
|
3715
|
+
* type: 'array',
|
|
3716
|
+
* fields: [
|
|
3717
|
+
* [{ key: 'label', type: 'input', value: 'Structured' }], // Object item
|
|
3718
|
+
* { key: 'value', type: 'input', value: 'Simple' }, // Primitive item
|
|
3719
|
+
* ]
|
|
3684
3720
|
* }
|
|
3685
|
-
* ```
|
|
3686
|
-
*/
|
|
3687
|
-
type NarrowField<T> = T extends {
|
|
3688
|
-
type: infer TType;
|
|
3689
|
-
} ? (TType extends AvailableFieldTypes ? ExtractField<TType> : T) : T;
|
|
3690
|
-
/**
|
|
3691
|
-
* Narrow each field in an array based on its `type` property.
|
|
3692
|
-
* Use with `satisfies` to get proper type inference for field arrays.
|
|
3693
3721
|
*
|
|
3694
3722
|
* @example
|
|
3695
|
-
*
|
|
3696
|
-
*
|
|
3697
|
-
*
|
|
3698
|
-
*
|
|
3699
|
-
* ]
|
|
3700
|
-
*
|
|
3701
|
-
*/
|
|
3702
|
-
type NarrowFields = readonly NarrowField<RegisteredFieldTypes>[];
|
|
3703
|
-
|
|
3704
|
-
/**
|
|
3705
|
-
* Type constraints for field nesting rules
|
|
3706
|
-
* These ensure that container fields can only contain valid child field types
|
|
3723
|
+
* // Empty array (add items via buttons)
|
|
3724
|
+
* {
|
|
3725
|
+
* key: 'tags',
|
|
3726
|
+
* type: 'array',
|
|
3727
|
+
* fields: [] // No initial items - user adds via button with template
|
|
3728
|
+
* }
|
|
3707
3729
|
*
|
|
3708
|
-
*
|
|
3709
|
-
|
|
3710
|
-
/**
|
|
3711
|
-
* Fields that are allowed as children of Page fields
|
|
3712
|
-
* Pages can contain: rows, groups, arrays, and leaf fields (but NOT other pages)
|
|
3713
|
-
*/
|
|
3714
|
-
type PageAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
3715
|
-
/**
|
|
3716
|
-
* Fields that are allowed as children of Group fields
|
|
3717
|
-
* Groups can contain: rows and leaf fields (but NOT pages or other groups)
|
|
3718
|
-
*/
|
|
3719
|
-
type GroupAllowedChildren = LeafFieldTypes | RowField | ContainerField;
|
|
3720
|
-
/**
|
|
3721
|
-
* Fields that are allowed as children of Array fields
|
|
3722
|
-
* Arrays can contain: rows, groups, and leaf fields (but NOT pages or other arrays)
|
|
3723
|
-
* Groups are used for creating object arrays where each array item is an object
|
|
3724
|
-
*/
|
|
3725
|
-
type ArrayAllowedChildren = LeafFieldTypes | RowField | GroupField | ContainerField;
|
|
3726
|
-
/**
|
|
3727
|
-
* Fields that are allowed as children of Container fields.
|
|
3728
|
-
* Containers are pure layout primitives that flatten their children into the
|
|
3729
|
-
* parent form, so most registered field types may appear inside — including
|
|
3730
|
-
* hidden fields and other containers — but NOT pages.
|
|
3731
|
-
*/
|
|
3732
|
-
type ContainerAllowedChildren = LeafFieldTypes | RowField | GroupField | ArrayField | SimplifiedArrayField | ContainerField;
|
|
3733
|
-
/**
|
|
3734
|
-
* Row is a synthetic field type that resolves to a Container at runtime, so
|
|
3735
|
-
* it accepts the same children as a Container.
|
|
3736
|
-
*/
|
|
3737
|
-
type RowAllowedChildren = ContainerAllowedChildren;
|
|
3738
|
-
|
|
3739
|
-
/**
|
|
3740
|
-
* Row field interface for creating horizontal layouts.
|
|
3741
|
-
* A row is a synthetic field type that resolves to a Container at runtime,
|
|
3742
|
-
* with a synthesized `{ type: 'row' }` wrapper applied for layout. The row
|
|
3743
|
-
* itself does not hold a value — its children flatten into the parent form.
|
|
3730
|
+
* Nesting constraints: Arrays can contain rows, leaf fields, or groups (for object arrays),
|
|
3731
|
+
* but NOT pages or other arrays. Runtime validation enforces these rules.
|
|
3744
3732
|
*
|
|
3745
|
-
* Note:
|
|
3733
|
+
* Note: Arrays are container fields and do not support `meta` since they have no native form element.
|
|
3746
3734
|
*/
|
|
3747
|
-
interface
|
|
3748
|
-
type: '
|
|
3749
|
-
/**
|
|
3735
|
+
interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never> {
|
|
3736
|
+
type: 'array';
|
|
3737
|
+
/**
|
|
3738
|
+
* Array of item definitions. Each element defines one array item:
|
|
3739
|
+
* - Single FieldDef = primitive item (field's value is extracted directly)
|
|
3740
|
+
* - Array of FieldDefs = object item (fields are merged into an object)
|
|
3741
|
+
* - Empty array `[]` = no initial items (add via buttons with explicit templates)
|
|
3742
|
+
* - Items can have different configurations (heterogeneous arrays supported)
|
|
3743
|
+
* - Initial values are set via `value` property on each field definition
|
|
3744
|
+
*/
|
|
3750
3745
|
readonly fields: TFields;
|
|
3751
|
-
/**
|
|
3746
|
+
/** Array fields do not have a label property **/
|
|
3752
3747
|
readonly label?: never;
|
|
3753
|
-
/**
|
|
3748
|
+
/** Arrays do not support meta - they have no native form element **/
|
|
3754
3749
|
readonly meta?: never;
|
|
3755
3750
|
/**
|
|
3756
|
-
* Logic configurations for conditional
|
|
3757
|
-
* Only 'hidden' type logic is supported for
|
|
3751
|
+
* Logic configurations for conditional array visibility.
|
|
3752
|
+
* Only 'hidden' type logic is supported for arrays.
|
|
3758
3753
|
*/
|
|
3759
3754
|
readonly logic?: ContainerLogicConfig[];
|
|
3755
|
+
/**
|
|
3756
|
+
* Minimum number of items required in the array.
|
|
3757
|
+
* Validation fails if the array has fewer items than this value.
|
|
3758
|
+
*/
|
|
3759
|
+
readonly minLength?: number;
|
|
3760
|
+
/**
|
|
3761
|
+
* Maximum number of items allowed in the array.
|
|
3762
|
+
* Validation fails if the array has more items than this value.
|
|
3763
|
+
*/
|
|
3764
|
+
readonly maxLength?: number;
|
|
3760
3765
|
}
|
|
3761
3766
|
/**
|
|
3762
|
-
* Type guard for
|
|
3767
|
+
* Type guard for ArrayField with proper type narrowing.
|
|
3768
|
+
* Validates that the field is an array type with a fields property that is an array.
|
|
3769
|
+
* Fields can contain either single FieldDefs (primitive items) or arrays of FieldDefs (object items).
|
|
3763
3770
|
*/
|
|
3764
|
-
declare function
|
|
3765
|
-
|
|
3771
|
+
declare function isArrayField(field: FieldDef<any>): field is ArrayField;
|
|
3766
3772
|
/**
|
|
3767
|
-
*
|
|
3773
|
+
* Configuration for auto-generated add/remove buttons in simplified array fields.
|
|
3774
|
+
*/
|
|
3775
|
+
interface ArrayButtonConfig {
|
|
3776
|
+
/** Custom label for the button */
|
|
3777
|
+
readonly label?: string;
|
|
3778
|
+
/** Additional properties passed to the button component */
|
|
3779
|
+
readonly props?: Record<string, unknown>;
|
|
3780
|
+
}
|
|
3781
|
+
/**
|
|
3782
|
+
* Simplified array field interface for common use cases.
|
|
3768
3783
|
*
|
|
3769
|
-
*
|
|
3770
|
-
* the
|
|
3771
|
-
*
|
|
3784
|
+
* Instead of manually specifying each item in `fields`, provide a `template` that defines
|
|
3785
|
+
* the structure of a single item, and a `value` array with initial data.
|
|
3786
|
+
* The library auto-generates add/remove buttons.
|
|
3772
3787
|
*
|
|
3773
|
-
*
|
|
3774
|
-
* to produce a discriminated union of all registered wrapper configs.
|
|
3788
|
+
* Discriminant: `template` presence → simplified API; `Array.isArray(template)` → object vs primitive.
|
|
3775
3789
|
*
|
|
3776
3790
|
* @example
|
|
3777
|
-
*
|
|
3778
|
-
*
|
|
3779
|
-
*
|
|
3780
|
-
*
|
|
3781
|
-
*
|
|
3782
|
-
*
|
|
3783
|
-
*
|
|
3784
|
-
*/
|
|
3785
|
-
type WrapperConfig<TWrappers extends RegisteredWrapperTypes = RegisteredWrapperTypes> = TWrappers extends keyof FieldRegistryWrappers ? FieldRegistryWrappers[TWrappers] : {
|
|
3786
|
-
readonly type: TWrappers;
|
|
3787
|
-
};
|
|
3788
|
-
/**
|
|
3789
|
-
* Signature of a lazy component loader — either a direct component class or
|
|
3790
|
-
* an ES module whose `default` export is the component. Shared by field and
|
|
3791
|
-
* wrapper registrations so the `loadComponent: () => import('./x.component')`
|
|
3792
|
-
* idiom types cleanly in both places.
|
|
3793
|
-
*/
|
|
3794
|
-
type LazyComponentLoader<T = unknown> = () => Promise<Type<T> | {
|
|
3795
|
-
default: Type<T>;
|
|
3796
|
-
}>;
|
|
3797
|
-
/**
|
|
3798
|
-
* Configuration interface for registering wrapper types.
|
|
3791
|
+
* // Primitive array: ['angular', 'typescript']
|
|
3792
|
+
* {
|
|
3793
|
+
* key: 'tags',
|
|
3794
|
+
* type: 'array',
|
|
3795
|
+
* template: { key: 'value', type: 'input', label: 'Tag', required: true },
|
|
3796
|
+
* value: ['angular', 'typescript']
|
|
3797
|
+
* }
|
|
3799
3798
|
*
|
|
3800
|
-
*
|
|
3801
|
-
*
|
|
3802
|
-
*
|
|
3799
|
+
* @example
|
|
3800
|
+
* // Object array: [{ name: 'Jane', phone: '555' }]
|
|
3801
|
+
* {
|
|
3802
|
+
* key: 'contacts',
|
|
3803
|
+
* type: 'array',
|
|
3804
|
+
* template: [
|
|
3805
|
+
* { key: 'name', type: 'input', label: 'Contact Name', required: true },
|
|
3806
|
+
* { key: 'phone', type: 'input', label: 'Phone Number' },
|
|
3807
|
+
* ],
|
|
3808
|
+
* value: [{ name: 'Jane', phone: '555' }]
|
|
3809
|
+
* }
|
|
3803
3810
|
*
|
|
3804
3811
|
* @example
|
|
3805
|
-
*
|
|
3806
|
-
*
|
|
3807
|
-
*
|
|
3808
|
-
*
|
|
3809
|
-
* }
|
|
3810
|
-
*
|
|
3812
|
+
* // Button customization / opt-out
|
|
3813
|
+
* {
|
|
3814
|
+
* key: 'tags',
|
|
3815
|
+
* type: 'array',
|
|
3816
|
+
* template: { key: 'value', type: 'input', label: 'Tag' },
|
|
3817
|
+
* addButton: { label: 'Add Tag', props: { color: 'primary' } },
|
|
3818
|
+
* removeButton: false
|
|
3819
|
+
* }
|
|
3811
3820
|
*/
|
|
3812
|
-
interface
|
|
3813
|
-
|
|
3814
|
-
wrapperName: string;
|
|
3815
|
-
/** Wrapper definition type marker (internal use) */
|
|
3816
|
-
_wrapper?: T;
|
|
3821
|
+
interface SimplifiedArrayField extends FieldDef<never> {
|
|
3822
|
+
type: 'array';
|
|
3817
3823
|
/**
|
|
3818
|
-
*
|
|
3819
|
-
*
|
|
3824
|
+
* Template defining the structure of a single array item.
|
|
3825
|
+
* - Single field (ArrayAllowedChildren) → primitive array (each item is a single value)
|
|
3826
|
+
* - Array of fields (readonly ArrayAllowedChildren[]) → object array (each item is an object)
|
|
3820
3827
|
*/
|
|
3821
|
-
|
|
3828
|
+
readonly template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
|
|
3829
|
+
/** Initial values for the array. Each element creates one array item. */
|
|
3830
|
+
readonly value?: readonly unknown[];
|
|
3822
3831
|
/**
|
|
3823
|
-
*
|
|
3824
|
-
*
|
|
3825
|
-
* When a field's `type` matches any entry, the wrapper is injected into that
|
|
3826
|
-
* field's effective wrapper chain at the lowest priority (can be overridden
|
|
3827
|
-
* by `FormConfig.defaultWrappers` or the field-level `wrappers` array, and
|
|
3828
|
-
* fully cleared with `wrappers: null`).
|
|
3832
|
+
* Configuration for the auto-generated "Add" button, or `false` to disable it.
|
|
3833
|
+
* Defaults to a button with label "Add".
|
|
3829
3834
|
*/
|
|
3830
|
-
|
|
3835
|
+
readonly addButton?: ArrayButtonConfig | false;
|
|
3836
|
+
/**
|
|
3837
|
+
* Configuration for the auto-generated "Remove" button on each item, or `false` to disable it.
|
|
3838
|
+
* Defaults to a button with label "Remove".
|
|
3839
|
+
*/
|
|
3840
|
+
readonly removeButton?: ArrayButtonConfig | false;
|
|
3841
|
+
/** Simplified arrays do not support the label property */
|
|
3842
|
+
readonly label?: never;
|
|
3843
|
+
/** Simplified arrays do not support meta */
|
|
3844
|
+
readonly meta?: never;
|
|
3845
|
+
/**
|
|
3846
|
+
* Logic configurations for conditional array visibility.
|
|
3847
|
+
* Only 'hidden' type logic is supported for arrays.
|
|
3848
|
+
*/
|
|
3849
|
+
readonly logic?: ContainerLogicConfig[];
|
|
3850
|
+
/**
|
|
3851
|
+
* Minimum number of items required in the array.
|
|
3852
|
+
* Validation fails if the array has fewer items than this value.
|
|
3853
|
+
*/
|
|
3854
|
+
readonly minLength?: number;
|
|
3855
|
+
/**
|
|
3856
|
+
* Maximum number of items allowed in the array.
|
|
3857
|
+
* Validation fails if the array has more items than this value.
|
|
3858
|
+
*/
|
|
3859
|
+
readonly maxLength?: number;
|
|
3860
|
+
/** Mutually exclusive with `template` — use `fields` for the full API instead */
|
|
3861
|
+
readonly fields?: never;
|
|
3831
3862
|
}
|
|
3832
3863
|
/**
|
|
3833
|
-
* Type guard for
|
|
3864
|
+
* Type guard for SimplifiedArrayField.
|
|
3865
|
+
* Checks for `type: 'array'` with a `template` property (discriminant from full ArrayField).
|
|
3866
|
+
*/
|
|
3867
|
+
declare function isSimplifiedArrayField(field: FieldDef<any>): field is SimplifiedArrayField;
|
|
3868
|
+
|
|
3869
|
+
/**
|
|
3870
|
+
* Container fields registry - augment this interface to add custom container fields
|
|
3834
3871
|
*
|
|
3835
|
-
*
|
|
3872
|
+
* @example
|
|
3873
|
+
* ```typescript
|
|
3874
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
3875
|
+
* interface FieldRegistryContainers {
|
|
3876
|
+
* 'my-container': MyContainerFieldDef;
|
|
3877
|
+
* }
|
|
3878
|
+
* }
|
|
3879
|
+
* ```
|
|
3836
3880
|
*/
|
|
3837
|
-
|
|
3881
|
+
interface FieldRegistryContainers {
|
|
3882
|
+
page: PageField;
|
|
3883
|
+
row: RowField;
|
|
3884
|
+
group: GroupField;
|
|
3885
|
+
array: ArrayField | SimplifiedArrayField;
|
|
3886
|
+
container: ContainerField;
|
|
3887
|
+
}
|
|
3838
3888
|
/**
|
|
3839
|
-
*
|
|
3889
|
+
* Leaf fields registry - augment this interface to add custom leaf fields
|
|
3840
3890
|
*
|
|
3841
|
-
*
|
|
3842
|
-
*
|
|
3843
|
-
*
|
|
3844
|
-
*
|
|
3845
|
-
*
|
|
3846
|
-
*
|
|
3847
|
-
*
|
|
3891
|
+
* @example
|
|
3892
|
+
* ```typescript
|
|
3893
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
3894
|
+
* interface FieldRegistryLeaves {
|
|
3895
|
+
* 'my-input': MyInputFieldDef;
|
|
3896
|
+
* }
|
|
3897
|
+
* }
|
|
3898
|
+
* ```
|
|
3899
|
+
*/
|
|
3900
|
+
interface FieldRegistryLeaves {
|
|
3901
|
+
text: TextField;
|
|
3902
|
+
hidden: HiddenField;
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Wrapper type registry for module augmentation.
|
|
3906
|
+
*
|
|
3907
|
+
* Augment this interface to add type-safe wrapper type definitions.
|
|
3848
3908
|
*
|
|
3849
3909
|
* @example
|
|
3850
3910
|
* ```typescript
|
|
3851
|
-
* @
|
|
3852
|
-
*
|
|
3853
|
-
*
|
|
3854
|
-
*
|
|
3855
|
-
* </dbx-section>
|
|
3856
|
-
* `,
|
|
3857
|
-
* })
|
|
3858
|
-
* export class SectionWrapperComponent implements FieldWrapper {
|
|
3859
|
-
* readonly fieldComponent = viewChild.required('fieldComponent', { read: ViewContainerRef });
|
|
3860
|
-
* readonly header = input<string>();
|
|
3861
|
-
* readonly fieldInputs = input<WrapperFieldInputs>();
|
|
3911
|
+
* declare module '@ng-forge/dynamic-forms' {
|
|
3912
|
+
* interface FieldRegistryWrappers {
|
|
3913
|
+
* 'section': MySectionFieldDef
|
|
3914
|
+
* }
|
|
3862
3915
|
* }
|
|
3863
3916
|
* ```
|
|
3864
3917
|
*/
|
|
3865
|
-
interface
|
|
3866
|
-
|
|
3867
|
-
|
|
3918
|
+
interface FieldRegistryWrappers {
|
|
3919
|
+
css: CssWrapper;
|
|
3920
|
+
row: RowWrapper;
|
|
3868
3921
|
}
|
|
3869
3922
|
/**
|
|
3870
|
-
*
|
|
3923
|
+
* Global interface for dynamic form field definitions with categorization
|
|
3924
|
+
* This interface combines containers and leaves from their respective registries
|
|
3871
3925
|
*
|
|
3872
|
-
*
|
|
3873
|
-
*
|
|
3874
|
-
*
|
|
3926
|
+
* Container fields: Layout fields that contain other fields (page, row, group, array)
|
|
3927
|
+
* Leaf fields: Fields that can hold values or display content (input, text, etc.)
|
|
3928
|
+
*
|
|
3929
|
+
* To add custom fields, augment FieldRegistryContainers or FieldRegistryLeaves
|
|
3875
3930
|
*/
|
|
3876
|
-
|
|
3931
|
+
interface DynamicFormFieldRegistry {
|
|
3932
|
+
/**
|
|
3933
|
+
* Container fields that hold other fields (no value, have children)
|
|
3934
|
+
*/
|
|
3935
|
+
containers: FieldRegistryContainers;
|
|
3936
|
+
/**
|
|
3937
|
+
* Leaf fields that have values or display content
|
|
3938
|
+
*/
|
|
3939
|
+
leaves: FieldRegistryLeaves;
|
|
3940
|
+
/**
|
|
3941
|
+
* Field wrappers types
|
|
3942
|
+
*/
|
|
3943
|
+
wrappers: FieldRegistryWrappers;
|
|
3944
|
+
}
|
|
3945
|
+
/**
|
|
3946
|
+
* Union type of all registered container field definitions
|
|
3947
|
+
*/
|
|
3948
|
+
type ContainerFieldTypes = DynamicFormFieldRegistry['containers'][keyof DynamicFormFieldRegistry['containers']];
|
|
3949
|
+
/**
|
|
3950
|
+
* Union type of all registered leaf field definitions
|
|
3951
|
+
*/
|
|
3952
|
+
type LeafFieldTypes = DynamicFormFieldRegistry['leaves'][keyof DynamicFormFieldRegistry['leaves']];
|
|
3953
|
+
/**
|
|
3954
|
+
* Union type of all registered field definitions
|
|
3955
|
+
*/
|
|
3956
|
+
type RegisteredFieldTypes = ContainerFieldTypes | LeafFieldTypes;
|
|
3957
|
+
/**
|
|
3958
|
+
* Extract field types that are available in the registry
|
|
3959
|
+
*/
|
|
3960
|
+
type AvailableFieldTypes = keyof DynamicFormFieldRegistry['containers'] | keyof DynamicFormFieldRegistry['leaves'];
|
|
3961
|
+
/**
|
|
3962
|
+
* Extract wrapper types that are available in the registry
|
|
3963
|
+
*/
|
|
3964
|
+
type RegisteredWrapperTypes = keyof DynamicFormFieldRegistry['wrappers'];
|
|
3965
|
+
/**
|
|
3966
|
+
* Combined registry mapping type names to field definitions.
|
|
3967
|
+
* This flattens containers and leaves into a single mapping.
|
|
3968
|
+
*/
|
|
3969
|
+
type FieldTypeMap = DynamicFormFieldRegistry['containers'] & DynamicFormFieldRegistry['leaves'];
|
|
3970
|
+
/**
|
|
3971
|
+
* Extract a specific field type from RegisteredFieldTypes based on the `type` discriminant.
|
|
3972
|
+
* This enables proper type narrowing when defining fields.
|
|
3973
|
+
*
|
|
3974
|
+
* @example
|
|
3975
|
+
* ```typescript
|
|
3976
|
+
* // Extract a specific field type
|
|
3977
|
+
* type MyInputField = ExtractField<'input'>;
|
|
3978
|
+
*
|
|
3979
|
+
* // Use in field definitions for proper props inference
|
|
3980
|
+
* const field: ExtractField<'input'> = {
|
|
3981
|
+
* type: 'input',
|
|
3982
|
+
* key: 'email',
|
|
3983
|
+
* value: '',
|
|
3984
|
+
* props: { type: 'email' } // Only input props allowed here
|
|
3985
|
+
* };
|
|
3986
|
+
* ```
|
|
3987
|
+
*/
|
|
3988
|
+
type ExtractField<T extends AvailableFieldTypes> = T extends keyof FieldTypeMap ? FieldTypeMap[T] : never;
|
|
3989
|
+
/**
|
|
3990
|
+
* Narrow a field definition based on its `type` property.
|
|
3991
|
+
* Use this to get proper type inference when working with field unions.
|
|
3992
|
+
*
|
|
3993
|
+
* @example
|
|
3994
|
+
* ```typescript
|
|
3995
|
+
* function processField<T extends RegisteredFieldTypes>(field: T): NarrowField<T> {
|
|
3996
|
+
* return field as NarrowField<T>;
|
|
3997
|
+
* }
|
|
3998
|
+
* ```
|
|
3999
|
+
*/
|
|
4000
|
+
type NarrowField<T> = T extends {
|
|
4001
|
+
type: infer TType;
|
|
4002
|
+
} ? (TType extends AvailableFieldTypes ? ExtractField<TType> : T) : T;
|
|
4003
|
+
/**
|
|
4004
|
+
* Narrow each field in an array based on its `type` property.
|
|
4005
|
+
* Use with `satisfies` to get proper type inference for field arrays.
|
|
4006
|
+
*
|
|
4007
|
+
* @example
|
|
4008
|
+
* ```typescript
|
|
4009
|
+
* const fields = [
|
|
4010
|
+
* { type: 'input', key: 'name', value: '', props: { type: 'text' } },
|
|
4011
|
+
* { type: 'select', key: 'country', value: 'us', options: [...] },
|
|
4012
|
+
* ] as const satisfies NarrowFields;
|
|
4013
|
+
* ```
|
|
4014
|
+
*/
|
|
4015
|
+
type NarrowFields = readonly NarrowField<RegisteredFieldTypes>[];
|
|
3877
4016
|
|
|
3878
4017
|
/**
|
|
3879
4018
|
* Base interface for native HTML attributes that can be passed to form field elements.
|
|
@@ -3974,18 +4113,26 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
|
|
|
3974
4113
|
/**
|
|
3975
4114
|
* Field type identifier for component selection.
|
|
3976
4115
|
*
|
|
3977
|
-
* Determines which component will be rendered for this field.
|
|
3978
|
-
*
|
|
4116
|
+
* Determines which component will be rendered for this field. Names
|
|
4117
|
+
* registered through module augmentation of `DynamicFormFieldRegistry`
|
|
4118
|
+
* are surfaced as IDE autocomplete suggestions via `RegisteredFieldTypes`;
|
|
4119
|
+
* the `(string & {})` branch keeps arbitrary custom identifiers accepted
|
|
4120
|
+
* as an escape hatch for fields that haven't been augmented into the
|
|
4121
|
+
* registry.
|
|
3979
4122
|
*
|
|
3980
4123
|
* @example
|
|
3981
4124
|
* ```typescript
|
|
3982
|
-
* type: 'input' // Text input field
|
|
3983
|
-
* type: 'select' // Dropdown selection
|
|
3984
|
-
* type: 'checkbox' // Boolean checkbox
|
|
3985
|
-
* type: 'group' // Field group container
|
|
4125
|
+
* type: 'input' // Text input field (registered)
|
|
4126
|
+
* type: 'select' // Dropdown selection (registered)
|
|
4127
|
+
* type: 'checkbox' // Boolean checkbox (registered)
|
|
4128
|
+
* type: 'group' // Field group container (registered)
|
|
4129
|
+
* type: 'my-custom' // Unregistered — still accepted, no autocomplete
|
|
3986
4130
|
* ```
|
|
4131
|
+
*
|
|
4132
|
+
* Wrapper types are intentionally excluded — wrappers are configured through
|
|
4133
|
+
* `wrappers` / `withFormWrappers()` rather than via the field-type discriminant.
|
|
3987
4134
|
*/
|
|
3988
|
-
type: string;
|
|
4135
|
+
type: RegisteredFieldTypes['type'] | (string & {});
|
|
3989
4136
|
/**
|
|
3990
4137
|
* Human-readable field label displayed to users.
|
|
3991
4138
|
*
|
|
@@ -4188,6 +4335,24 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
|
|
|
4188
4335
|
* and any field-level `wrappers`.
|
|
4189
4336
|
*/
|
|
4190
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>;
|
|
4191
4356
|
}
|
|
4192
4357
|
type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
|
|
4193
4358
|
/**
|
|
@@ -4282,6 +4447,103 @@ interface GroupContext {
|
|
|
4282
4447
|
*/
|
|
4283
4448
|
type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
|
|
4284
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
|
+
|
|
4285
4547
|
/**
|
|
4286
4548
|
* Defines how a field type handles form values and data collection.
|
|
4287
4549
|
*
|
|
@@ -4374,7 +4636,24 @@ interface FieldTypeDefinition<T extends FieldDef<any> = any> {
|
|
|
4374
4636
|
/** Semantic scope for tooling to discover interchangeable field alternatives */
|
|
4375
4637
|
scope?: FieldScope | FieldScope[];
|
|
4376
4638
|
/** Mapped component inputs that must exist before the renderer instantiates the component */
|
|
4377
|
-
renderReadyWhen?: RenderReadyInput[];
|
|
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;
|
|
4378
4657
|
}
|
|
4379
4658
|
/**
|
|
4380
4659
|
* Injection token for the global field type registry.
|
|
@@ -4403,6 +4682,15 @@ interface ResolvedField {
|
|
|
4403
4682
|
injector: Injector;
|
|
4404
4683
|
inputs: Signal<Record<string, unknown>>;
|
|
4405
4684
|
renderReady: Signal<boolean>;
|
|
4685
|
+
/**
|
|
4686
|
+
* Whether the field is currently hidden. Sourced from the FieldTree's
|
|
4687
|
+
* `state.hidden()` for value-bearing fields (set by Angular Signal Forms in
|
|
4688
|
+
* response to `hidden(path, …)` calls), or from the mapper's explicit
|
|
4689
|
+
* `hidden` input for non-value fields (button, text, container, …). Consumers
|
|
4690
|
+
* must `@if (!field.hidden())` around `*dfFieldOutlet` so hidden fields are
|
|
4691
|
+
* removed from the DOM — Angular Signal Forms warns (NG01916) otherwise.
|
|
4692
|
+
*/
|
|
4693
|
+
hidden: Signal<boolean>;
|
|
4406
4694
|
}
|
|
4407
4695
|
|
|
4408
4696
|
/**
|
|
@@ -4610,12 +4898,22 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
4610
4898
|
formOptions: _angular_core.InputSignal<FormOptions | undefined>;
|
|
4611
4899
|
/** Form values for two-way data binding. */
|
|
4612
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">;
|
|
4613
4908
|
private destroyRef;
|
|
4614
4909
|
private injector;
|
|
4615
4910
|
protected environmentInjector: EnvironmentInjector;
|
|
4616
4911
|
private eventBus;
|
|
4617
4912
|
private logger;
|
|
4618
4913
|
private dispatcher;
|
|
4914
|
+
private templateRegistry;
|
|
4915
|
+
/** All `<ng-template dfTemplate="...">` instances projected into this form. */
|
|
4916
|
+
private readonly _projectedTemplates;
|
|
4619
4917
|
/** State manager that owns all form state. Initialized via connectDeps() to guarantee
|
|
4620
4918
|
* FORM_STATE_DEPS is populated before FormStateManager is injected. */
|
|
4621
4919
|
private readonly stateManager;
|
|
@@ -4729,7 +5027,7 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
4729
5027
|
private connectDeps;
|
|
4730
5028
|
private setupEventHandlers;
|
|
4731
5029
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
|
|
4732
|
-
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>;
|
|
4733
5031
|
}
|
|
4734
5032
|
|
|
4735
5033
|
declare class EventBus {
|
|
@@ -4904,214 +5202,6 @@ interface DynamicFormFeature<TKind extends string = string> {
|
|
|
4904
5202
|
ɵproviders: Provider[];
|
|
4905
5203
|
}
|
|
4906
5204
|
|
|
4907
|
-
interface FieldOption<T = unknown> {
|
|
4908
|
-
label: DynamicText;
|
|
4909
|
-
value: T;
|
|
4910
|
-
disabled?: boolean;
|
|
4911
|
-
[key: string]: unknown;
|
|
4912
|
-
}
|
|
4913
|
-
|
|
4914
|
-
/**
|
|
4915
|
-
* Injection token for form-level default wrappers.
|
|
4916
|
-
*
|
|
4917
|
-
* Provides a Signal of the `defaultWrappers` array from FormConfig. Consumed by
|
|
4918
|
-
* `DfFieldOutlet` / `ContainerFieldComponent` to merge into each field's
|
|
4919
|
-
* effective wrapper chain (lowest priority after auto-associations, higher
|
|
4920
|
-
* than field-level `wrappers`).
|
|
4921
|
-
*
|
|
4922
|
-
* Provided once at the DynamicForm level and inherited via Angular's
|
|
4923
|
-
* hierarchical injector.
|
|
4924
|
-
*/
|
|
4925
|
-
declare const DEFAULT_WRAPPERS: InjectionToken<Signal<readonly WrapperConfig[] | undefined>>;
|
|
4926
|
-
/**
|
|
4927
|
-
* Injection token for providing field signal context to mappers and components.
|
|
4928
|
-
*
|
|
4929
|
-
* The field signal context is the "nervous system" of the dynamic form library,
|
|
4930
|
-
* providing access to:
|
|
4931
|
-
* - The form instance and structure
|
|
4932
|
-
* - Current form values (as signals)
|
|
4933
|
-
* - Default values
|
|
4934
|
-
* - Validation messages
|
|
4935
|
-
* - The injector for creating components
|
|
4936
|
-
*
|
|
4937
|
-
* This token is provided by the DynamicForm component and can be scoped
|
|
4938
|
-
* for nested forms (Groups, Arrays) via child injectors.
|
|
4939
|
-
*
|
|
4940
|
-
* @example
|
|
4941
|
-
* ```typescript
|
|
4942
|
-
* // In a mapper function
|
|
4943
|
-
* export function mapValueField(fieldDef: BaseValueField<any, any>): Binding[] {
|
|
4944
|
-
* const context = inject(FIELD_SIGNAL_CONTEXT);
|
|
4945
|
-
* const form = context.form();
|
|
4946
|
-
* // ... use context
|
|
4947
|
-
* }
|
|
4948
|
-
*
|
|
4949
|
-
* // In a component
|
|
4950
|
-
* export class MyFieldComponent {
|
|
4951
|
-
* private context = inject(FIELD_SIGNAL_CONTEXT);
|
|
4952
|
-
* }
|
|
4953
|
-
* ```
|
|
4954
|
-
*/
|
|
4955
|
-
declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
|
|
4956
|
-
/**
|
|
4957
|
-
* Injection token for providing array context metadata to mappers and components.
|
|
4958
|
-
*
|
|
4959
|
-
* This token is optionally provided by ArrayFieldComponent when creating injectors
|
|
4960
|
-
* for array items. It contains metadata about the array item's position and parent.
|
|
4961
|
-
*
|
|
4962
|
-
* Mappers can inject this token with {optional: true} to access array context:
|
|
4963
|
-
*
|
|
4964
|
-
* @example
|
|
4965
|
-
* ```typescript
|
|
4966
|
-
* // In a mapper function
|
|
4967
|
-
* export function buttonMapper(fieldDef: FieldDef<any>): Binding[] {
|
|
4968
|
-
* const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
|
|
4969
|
-
* if (arrayContext) {
|
|
4970
|
-
* // Use arrayContext.index, arrayContext.arrayKey, etc.
|
|
4971
|
-
* }
|
|
4972
|
-
* }
|
|
4973
|
-
* ```
|
|
4974
|
-
*/
|
|
4975
|
-
declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
|
|
4976
|
-
/**
|
|
4977
|
-
* Injection token for providing group ancestry to mappers and components.
|
|
4978
|
-
*
|
|
4979
|
-
* Re-provided in two places:
|
|
4980
|
-
* 1. `GroupFieldComponent` provides a context whose `groupPath()` is the
|
|
4981
|
-
* parent path extended by its own key (e.g. `'address'` or `'user.address'`).
|
|
4982
|
-
* 2. `createArrayItemInjector` provides a sentinel with an empty `groupPath`,
|
|
4983
|
-
* resetting the chain at array boundaries — descendants of an array item
|
|
4984
|
-
* that re-enter a group compose paths INSIDE the array item, matching the
|
|
4985
|
-
* property-derivation collector's keying.
|
|
4986
|
-
*
|
|
4987
|
-
* `mapFieldToInputs` reads this to scope DOM IDs and override-store keys so
|
|
4988
|
-
* the same leaf key can appear in different groups without colliding (#401).
|
|
4989
|
-
*
|
|
4990
|
-
* Inject with `{ optional: true }` because top-level fields with no group
|
|
4991
|
-
* ancestor (and no enclosing array) won't have the token provided. The token
|
|
4992
|
-
* IS present for fields inside an array even when no inner group exists — the
|
|
4993
|
-
* array-boundary sentinel makes injection succeed with an empty `groupPath`.
|
|
4994
|
-
*/
|
|
4995
|
-
declare const GROUP_CONTEXT: InjectionToken<GroupContext>;
|
|
4996
|
-
/**
|
|
4997
|
-
* Injection token for form-level default props.
|
|
4998
|
-
*
|
|
4999
|
-
* Default props are form-wide property defaults that are merged with field-level props.
|
|
5000
|
-
* Field props take precedence over default props.
|
|
5001
|
-
*
|
|
5002
|
-
* Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
|
|
5003
|
-
* DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
|
|
5004
|
-
* via Angular's hierarchical injector.
|
|
5005
|
-
*
|
|
5006
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5007
|
-
* that read the signal inside their computed() will automatically update.
|
|
5008
|
-
*
|
|
5009
|
-
* @example
|
|
5010
|
-
* ```typescript
|
|
5011
|
-
* // In a mapper function
|
|
5012
|
-
* const defaultPropsSignal = inject(DEFAULT_PROPS);
|
|
5013
|
-
*
|
|
5014
|
-
* return computed(() => {
|
|
5015
|
-
* const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
|
|
5016
|
-
* const baseInputs = buildBaseInputs(fieldDef, defaultProps);
|
|
5017
|
-
* // ...
|
|
5018
|
-
* });
|
|
5019
|
-
* ```
|
|
5020
|
-
*/
|
|
5021
|
-
declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
|
|
5022
|
-
/**
|
|
5023
|
-
* Injection token for form-level default validation messages.
|
|
5024
|
-
*
|
|
5025
|
-
* Default validation messages act as fallbacks when fields have validation
|
|
5026
|
-
* errors but no field-level `validationMessages` defined.
|
|
5027
|
-
*
|
|
5028
|
-
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5029
|
-
* and inherited by all children via Angular's hierarchical injector.
|
|
5030
|
-
*
|
|
5031
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5032
|
-
* that read the signal inside their computed() will automatically update.
|
|
5033
|
-
*
|
|
5034
|
-
* @example
|
|
5035
|
-
* ```typescript
|
|
5036
|
-
* // In a mapper or component
|
|
5037
|
-
* const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
|
|
5038
|
-
*
|
|
5039
|
-
* return computed(() => {
|
|
5040
|
-
* const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
|
|
5041
|
-
* const message = defaultMessages?.required ?? 'Field is required';
|
|
5042
|
-
* // ...
|
|
5043
|
-
* });
|
|
5044
|
-
* ```
|
|
5045
|
-
*/
|
|
5046
|
-
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
5047
|
-
/**
|
|
5048
|
-
* Injection token for form-level options.
|
|
5049
|
-
*
|
|
5050
|
-
* Form options control form-wide behavior including button disabled states.
|
|
5051
|
-
* Used by button mappers to determine default disabled behavior.
|
|
5052
|
-
*
|
|
5053
|
-
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5054
|
-
* and inherited by all children via Angular's hierarchical injector.
|
|
5055
|
-
*
|
|
5056
|
-
* The token provides a Signal to enable reactivity - when config changes, mappers
|
|
5057
|
-
* that read the signal inside their computed() will automatically update.
|
|
5058
|
-
*
|
|
5059
|
-
* @example
|
|
5060
|
-
* ```typescript
|
|
5061
|
-
* // In a button mapper
|
|
5062
|
-
* const formOptionsSignal = inject(FORM_OPTIONS);
|
|
5063
|
-
*
|
|
5064
|
-
* return computed(() => {
|
|
5065
|
-
* const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
|
|
5066
|
-
* const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
|
|
5067
|
-
* // ...
|
|
5068
|
-
* });
|
|
5069
|
-
* ```
|
|
5070
|
-
*/
|
|
5071
|
-
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
5072
|
-
|
|
5073
|
-
/**
|
|
5074
|
-
* Type guard to check if a field is a container field (page, row, group, or array)
|
|
5075
|
-
* Container fields have a 'fields' property and don't contribute values directly.
|
|
5076
|
-
* This overload works with RegisteredFieldTypes for full type narrowing.
|
|
5077
|
-
*/
|
|
5078
|
-
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
5079
|
-
/**
|
|
5080
|
-
* Type guard to check if a field is a container field (page, row, group, or array).
|
|
5081
|
-
* This overload works with any FieldDef for looser type checking.
|
|
5082
|
-
*/
|
|
5083
|
-
declare function isContainerField(field: FieldDef<unknown>): boolean;
|
|
5084
|
-
/**
|
|
5085
|
-
* Type guard to check if a field is a leaf field (value or display field)
|
|
5086
|
-
* Leaf fields don't have children and either contribute values or display content
|
|
5087
|
-
*/
|
|
5088
|
-
declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
|
|
5089
|
-
/**
|
|
5090
|
-
* Type guard to check if a field has a value property (value-bearing field)
|
|
5091
|
-
* These fields contribute to the form value output
|
|
5092
|
-
* Note: Using `unknown` in the Extract condition to match any value type
|
|
5093
|
-
*/
|
|
5094
|
-
declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
|
|
5095
|
-
value: unknown;
|
|
5096
|
-
}>;
|
|
5097
|
-
/**
|
|
5098
|
-
* Type guard to check if a field is excluded from form values (display-only field)
|
|
5099
|
-
* Currently this includes text fields and any other fields without a value property
|
|
5100
|
-
*/
|
|
5101
|
-
declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
|
|
5102
|
-
|
|
5103
|
-
/**
|
|
5104
|
-
* Helper types for creating type-safe field configurations with proper nesting constraints
|
|
5105
|
-
*/
|
|
5106
|
-
|
|
5107
|
-
/**
|
|
5108
|
-
* Type helper for accessing nested field paths safely
|
|
5109
|
-
* This allows accessing child paths while maintaining some type safety
|
|
5110
|
-
*/
|
|
5111
|
-
type FieldPathAccess<TValue> = {
|
|
5112
|
-
[K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
|
|
5113
|
-
};
|
|
5114
|
-
|
|
5115
5205
|
/**
|
|
5116
5206
|
* User-facing wrapper registration shape used with `createWrappers(...)`.
|
|
5117
5207
|
*
|
|
@@ -5199,6 +5289,48 @@ type InferWrapperRegistry<T> = T extends WrappersBundle<infer R> ? {
|
|
|
5199
5289
|
};
|
|
5200
5290
|
} : never;
|
|
5201
5291
|
|
|
5292
|
+
/**
|
|
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.
|
|
5296
|
+
*/
|
|
5297
|
+
declare function isContainerField(field: RegisteredFieldTypes): field is ContainerFieldTypes;
|
|
5298
|
+
/**
|
|
5299
|
+
* Type guard to check if a field is a container field (page, row, group, or array).
|
|
5300
|
+
* This overload works with any FieldDef for looser type checking.
|
|
5301
|
+
*/
|
|
5302
|
+
declare function isContainerField(field: FieldDef<unknown>): boolean;
|
|
5303
|
+
/**
|
|
5304
|
+
* Type guard to check if a field is a leaf field (value or display field)
|
|
5305
|
+
* Leaf fields don't have children and either contribute values or display content
|
|
5306
|
+
*/
|
|
5307
|
+
declare function isLeafField(field: RegisteredFieldTypes): field is LeafFieldTypes;
|
|
5308
|
+
/**
|
|
5309
|
+
* Type guard to check if a field has a value property (value-bearing field)
|
|
5310
|
+
* These fields contribute to the form value output
|
|
5311
|
+
* Note: Using `unknown` in the Extract condition to match any value type
|
|
5312
|
+
*/
|
|
5313
|
+
declare function isValueBearingField(field: RegisteredFieldTypes): field is Extract<RegisteredFieldTypes, {
|
|
5314
|
+
value: unknown;
|
|
5315
|
+
}>;
|
|
5316
|
+
/**
|
|
5317
|
+
* Type guard to check if a field is excluded from form values (display-only field)
|
|
5318
|
+
* Currently this includes text fields and any other fields without a value property
|
|
5319
|
+
*/
|
|
5320
|
+
declare function isDisplayOnlyField(field: RegisteredFieldTypes): boolean;
|
|
5321
|
+
|
|
5322
|
+
/**
|
|
5323
|
+
* Helper types for creating type-safe field configurations with proper nesting constraints
|
|
5324
|
+
*/
|
|
5325
|
+
|
|
5326
|
+
/**
|
|
5327
|
+
* Type helper for accessing nested field paths safely
|
|
5328
|
+
* This allows accessing child paths while maintaining some type safety
|
|
5329
|
+
*/
|
|
5330
|
+
type FieldPathAccess<TValue> = {
|
|
5331
|
+
[K in keyof TValue]: SchemaPath<TValue[K]> | SchemaPathTree<TValue[K]>;
|
|
5332
|
+
};
|
|
5333
|
+
|
|
5202
5334
|
/**
|
|
5203
5335
|
* Extract FieldDef type from FieldTypeDefinition
|
|
5204
5336
|
*/
|
|
@@ -5237,390 +5369,1479 @@ type ExtractFieldTypes<T extends FieldTypeOrFeature[]> = {
|
|
|
5237
5369
|
* @param items - Field type definitions and/or features (like withLoggerConfig)
|
|
5238
5370
|
* @returns Environment providers for dependency injection with type inference
|
|
5239
5371
|
*
|
|
5240
|
-
* @example
|
|
5241
|
-
* ```typescript
|
|
5242
|
-
* // Application-level setup
|
|
5243
|
-
* bootstrapApplication(AppComponent, {
|
|
5244
|
-
* providers: [
|
|
5245
|
-
* provideDynamicForm(...withMaterialFields())
|
|
5246
|
-
* ]
|
|
5247
|
-
* });
|
|
5248
|
-
* ```
|
|
5372
|
+
* @example
|
|
5373
|
+
* ```typescript
|
|
5374
|
+
* // Application-level setup
|
|
5375
|
+
* bootstrapApplication(AppComponent, {
|
|
5376
|
+
* providers: [
|
|
5377
|
+
* provideDynamicForm(...withMaterialFields())
|
|
5378
|
+
* ]
|
|
5379
|
+
* });
|
|
5380
|
+
* ```
|
|
5381
|
+
*
|
|
5382
|
+
* @example
|
|
5383
|
+
* ```typescript
|
|
5384
|
+
* // Disable logging
|
|
5385
|
+
* bootstrapApplication(AppComponent, {
|
|
5386
|
+
* providers: [
|
|
5387
|
+
* provideDynamicForm(
|
|
5388
|
+
* ...withMaterialFields(),
|
|
5389
|
+
* withLoggerConfig(false)
|
|
5390
|
+
* )
|
|
5391
|
+
* ]
|
|
5392
|
+
* });
|
|
5393
|
+
* ```
|
|
5394
|
+
*
|
|
5395
|
+
* @example
|
|
5396
|
+
* ```typescript
|
|
5397
|
+
* // Custom field types with type inference
|
|
5398
|
+
* import { CustomFieldType, AnotherFieldType } from './custom-fields';
|
|
5399
|
+
*
|
|
5400
|
+
* export const appConfig: ApplicationConfig = {
|
|
5401
|
+
* providers: [
|
|
5402
|
+
* provideDynamicForm(CustomFieldType, AnotherFieldType)
|
|
5403
|
+
* ]
|
|
5404
|
+
* };
|
|
5405
|
+
* ```
|
|
5406
|
+
*
|
|
5407
|
+
* @typeParam T - Array of field type definitions for type inference
|
|
5408
|
+
*
|
|
5409
|
+
* @public
|
|
5410
|
+
*/
|
|
5411
|
+
declare function provideDynamicForm<const T extends FieldTypeOrFeature[]>(...items: T): ProvideDynamicFormResult<ExtractFieldTypes<T> extends FieldTypeDefinition[] ? ExtractFieldTypes<T> : FieldTypeDefinition[]>;
|
|
5412
|
+
/**
|
|
5413
|
+
* Extract FieldDef types from provider result
|
|
5414
|
+
*/
|
|
5415
|
+
type ExtractFieldDefs<T> = T extends {
|
|
5416
|
+
__fieldDefs?: infer F;
|
|
5417
|
+
} ? F : never;
|
|
5418
|
+
/**
|
|
5419
|
+
* Extract form value type from provider result
|
|
5420
|
+
*/
|
|
5421
|
+
type ExtractFormValue<T> = T extends {
|
|
5422
|
+
__formValue?: infer V;
|
|
5423
|
+
} ? V : never;
|
|
5424
|
+
|
|
5425
|
+
/**
|
|
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.
|
|
5438
|
+
*
|
|
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.
|
|
5446
|
+
*/
|
|
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;
|
|
5473
|
+
|
|
5474
|
+
/**
|
|
5475
|
+
* Fields every addon-action context carries, regardless of whether the
|
|
5476
|
+
* addon is bound to a host field or rendered orphaned.
|
|
5477
|
+
*/
|
|
5478
|
+
interface AddonActionContextBase<TValue> {
|
|
5479
|
+
/**
|
|
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.
|
|
5484
|
+
*/
|
|
5485
|
+
readonly field: {
|
|
5486
|
+
readonly key: string;
|
|
5487
|
+
readonly type: string;
|
|
5488
|
+
};
|
|
5489
|
+
/**
|
|
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.
|
|
5493
|
+
*/
|
|
5494
|
+
readonly value: TValue | undefined;
|
|
5495
|
+
}
|
|
5496
|
+
/**
|
|
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.
|
|
5500
|
+
*
|
|
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.
|
|
5526
|
+
*
|
|
5527
|
+
* @example
|
|
5528
|
+
* ```typescript
|
|
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.
|
|
5543
|
+
*
|
|
5544
|
+
* Adapter kinds that accept a `preset` field render the corresponding
|
|
5545
|
+
* behaviour:
|
|
5546
|
+
*
|
|
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`).
|
|
5556
|
+
*
|
|
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.
|
|
5563
|
+
*
|
|
5564
|
+
* Adapters add their canonical actions (e.g., Material's
|
|
5565
|
+
* `'mat-datepicker-open'`) without forking the `AddonActionPreset` union.
|
|
5566
|
+
*
|
|
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
|
+
}
|
|
5578
|
+
/**
|
|
5579
|
+
* Union of every preset name an addon may reference — universal presets
|
|
5580
|
+
* plus any contributed via {@link DynamicFormAddonActionPresetRegistry}.
|
|
5581
|
+
*
|
|
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.
|
|
5588
|
+
*/
|
|
5589
|
+
type AddonActionPreset = CommonAddonActionPreset | keyof DynamicFormAddonActionPresetRegistry | (string & {});
|
|
5590
|
+
/**
|
|
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
|
+
* ```
|
|
5605
|
+
*/
|
|
5606
|
+
interface DynamicFormActionRegistry {
|
|
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;
|
|
5614
|
+
|
|
5615
|
+
/**
|
|
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.
|
|
5628
|
+
*/
|
|
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;
|
|
5636
|
+
}
|
|
5637
|
+
|
|
5638
|
+
/**
|
|
5639
|
+
* Injection token for form-level default wrappers.
|
|
5640
|
+
*
|
|
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`).
|
|
5645
|
+
*
|
|
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
|
|
5665
|
+
* ```typescript
|
|
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
|
+
* }
|
|
5678
|
+
* ```
|
|
5679
|
+
*/
|
|
5680
|
+
declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<string, unknown>>>;
|
|
5681
|
+
/**
|
|
5682
|
+
* Throwing accessor for {@link FIELD_SIGNAL_CONTEXT}.
|
|
5683
|
+
*
|
|
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
|
|
5702
|
+
* ```typescript
|
|
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.
|
|
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
|
+
* // ...
|
|
5755
|
+
* });
|
|
5756
|
+
* ```
|
|
5757
|
+
*/
|
|
5758
|
+
declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
|
|
5759
|
+
/**
|
|
5760
|
+
* Injection token for form-level default validation messages.
|
|
5761
|
+
*
|
|
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
|
|
5772
|
+
* ```typescript
|
|
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
|
+
* });
|
|
5781
|
+
* ```
|
|
5782
|
+
*/
|
|
5783
|
+
declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
|
|
5784
|
+
/**
|
|
5785
|
+
* Injection token for form-level options.
|
|
5786
|
+
*
|
|
5787
|
+
* Form options control form-wide behavior including button disabled states.
|
|
5788
|
+
* Used by button mappers to determine default disabled behavior.
|
|
5789
|
+
*
|
|
5790
|
+
* Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
|
|
5791
|
+
* and inherited by all children via Angular's hierarchical injector.
|
|
5792
|
+
*
|
|
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
|
+
* ```
|
|
5807
|
+
*/
|
|
5808
|
+
declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
|
|
5809
|
+
|
|
5810
|
+
/**
|
|
5811
|
+
* Built-in field types provided by the dynamic form library.
|
|
5812
|
+
*
|
|
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.
|
|
5821
|
+
*
|
|
5822
|
+
* @public
|
|
5823
|
+
*/
|
|
5824
|
+
interface LoggerConfigOptions {
|
|
5825
|
+
/**
|
|
5826
|
+
* Whether general logging is enabled.
|
|
5827
|
+
*
|
|
5828
|
+
* @default true
|
|
5829
|
+
*/
|
|
5830
|
+
enabled?: boolean;
|
|
5831
|
+
/**
|
|
5832
|
+
* Derivation logging level.
|
|
5833
|
+
*
|
|
5834
|
+
* - `'none'`: No derivation debug logging (default)
|
|
5835
|
+
* - `'summary'`: Log cycle completion with counts
|
|
5836
|
+
* - `'verbose'`: Log individual derivation evaluations with details
|
|
5837
|
+
*
|
|
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.
|
|
5991
|
+
*/
|
|
5992
|
+
readonly excludeValueIfReadonly?: boolean;
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
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.
|
|
5249
6125
|
*
|
|
5250
|
-
* @example
|
|
6126
|
+
* @example Opt in once at the application root
|
|
5251
6127
|
* ```typescript
|
|
5252
|
-
* // Disable logging
|
|
5253
6128
|
* bootstrapApplication(AppComponent, {
|
|
5254
6129
|
* providers: [
|
|
5255
6130
|
* provideDynamicForm(
|
|
5256
6131
|
* ...withMaterialFields(),
|
|
5257
|
-
*
|
|
6132
|
+
* withLegacyStatusClasses()
|
|
5258
6133
|
* )
|
|
5259
6134
|
* ]
|
|
5260
6135
|
* });
|
|
5261
6136
|
* ```
|
|
5262
6137
|
*
|
|
5263
|
-
* @
|
|
5264
|
-
* ```typescript
|
|
5265
|
-
* // Custom field types with type inference
|
|
5266
|
-
* import { CustomFieldType, AnotherFieldType } from './custom-fields';
|
|
5267
|
-
*
|
|
5268
|
-
* export const appConfig: ApplicationConfig = {
|
|
5269
|
-
* providers: [
|
|
5270
|
-
* provideDynamicForm(CustomFieldType, AnotherFieldType)
|
|
5271
|
-
* ]
|
|
5272
|
-
* };
|
|
5273
|
-
* ```
|
|
5274
|
-
*
|
|
5275
|
-
* @typeParam T - Array of field type definitions for type inference
|
|
6138
|
+
* @returns A DynamicFormFeature that enables the legacy `ng-*` class strategy
|
|
5276
6139
|
*
|
|
5277
6140
|
* @public
|
|
5278
6141
|
*/
|
|
5279
|
-
declare function
|
|
5280
|
-
/**
|
|
5281
|
-
* Extract FieldDef types from provider result
|
|
5282
|
-
*/
|
|
5283
|
-
type ExtractFieldDefs<T> = T extends {
|
|
5284
|
-
__fieldDefs?: infer F;
|
|
5285
|
-
} ? F : never;
|
|
5286
|
-
/**
|
|
5287
|
-
* Extract form value type from provider result
|
|
5288
|
-
*/
|
|
5289
|
-
type ExtractFormValue<T> = T extends {
|
|
5290
|
-
__formValue?: infer V;
|
|
5291
|
-
} ? V : never;
|
|
6142
|
+
declare function withLegacyStatusClasses(): DynamicFormFeature<'legacy-status-classes'>;
|
|
5292
6143
|
|
|
5293
6144
|
/**
|
|
5294
|
-
*
|
|
6145
|
+
* Zero-cost type carrier used in wrapper registrations.
|
|
5295
6146
|
*
|
|
5296
|
-
*
|
|
5297
|
-
*
|
|
5298
|
-
*
|
|
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
|
+
* ```
|
|
5299
6166
|
*/
|
|
5300
|
-
declare
|
|
6167
|
+
declare function wrapperProps<T>(): T;
|
|
5301
6168
|
|
|
5302
6169
|
/**
|
|
5303
|
-
*
|
|
6170
|
+
* Input shape passed to a wrapper component via the `fieldInputs` input.
|
|
5304
6171
|
*
|
|
5305
|
-
*
|
|
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.
|
|
5306
6180
|
*/
|
|
5307
|
-
interface
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
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;
|
|
5314
6192
|
/**
|
|
5315
|
-
*
|
|
5316
|
-
*
|
|
5317
|
-
*
|
|
5318
|
-
* - `'summary'`: Log cycle completion with counts
|
|
5319
|
-
* - `'verbose'`: Log individual derivation evaluations with details
|
|
5320
|
-
*
|
|
5321
|
-
* @default 'none'
|
|
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.
|
|
5322
6196
|
*/
|
|
5323
|
-
|
|
6197
|
+
readonly setValue?: (next: unknown) => void;
|
|
6198
|
+
readonly [key: string]: unknown;
|
|
5324
6199
|
}
|
|
6200
|
+
|
|
5325
6201
|
/**
|
|
5326
|
-
*
|
|
6202
|
+
* Dispatcher component that renders an addon by looking up its `kind` in
|
|
6203
|
+
* `ADDON_KIND_REGISTRY` and instantiating the registered component.
|
|
5327
6204
|
*
|
|
5328
|
-
*
|
|
5329
|
-
* logging is disabled ('none'). Use this feature to enable derivation debugging.
|
|
6205
|
+
* Used by every adapter's field component to render slot content:
|
|
5330
6206
|
*
|
|
5331
|
-
*
|
|
5332
|
-
*
|
|
5333
|
-
*
|
|
5334
|
-
*
|
|
5335
|
-
*
|
|
5336
|
-
* withLoggerConfig(false)
|
|
5337
|
-
* )
|
|
6207
|
+
* ```html
|
|
6208
|
+
* \@for (a of addonsAt('prefix'); track $index) {
|
|
6209
|
+
* <df-addon-slot [addon]="a" />
|
|
6210
|
+
* }
|
|
6211
|
+
* ```
|
|
5338
6212
|
*
|
|
5339
|
-
*
|
|
5340
|
-
*
|
|
5341
|
-
*
|
|
5342
|
-
*
|
|
5343
|
-
* )
|
|
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.
|
|
5344
6308
|
*
|
|
5345
|
-
*
|
|
5346
|
-
*
|
|
5347
|
-
*
|
|
5348
|
-
* withLoggerConfig({ derivations: 'none' })
|
|
5349
|
-
* )
|
|
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.
|
|
5350
6312
|
*
|
|
5351
|
-
*
|
|
5352
|
-
*
|
|
5353
|
-
*
|
|
5354
|
-
*
|
|
5355
|
-
*
|
|
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>
|
|
5356
6320
|
* ```
|
|
5357
6321
|
*
|
|
5358
|
-
*
|
|
5359
|
-
*
|
|
5360
|
-
*
|
|
5361
|
-
*
|
|
6322
|
+
* In the field config:
|
|
6323
|
+
* ```ts
|
|
6324
|
+
* { slot: 'prefix', kind: 'template', templateKey: 'searchIcon' }
|
|
6325
|
+
* ```
|
|
5362
6326
|
*/
|
|
5363
|
-
declare
|
|
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
|
+
}
|
|
5364
6335
|
|
|
5365
6336
|
/**
|
|
5366
|
-
*
|
|
6337
|
+
* Renderer for the universal `text` addon kind.
|
|
5367
6338
|
*
|
|
5368
|
-
*
|
|
5369
|
-
*
|
|
5370
|
-
|
|
5371
|
-
declare const DynamicFormLogger: InjectionToken<Logger>;
|
|
5372
|
-
|
|
5373
|
-
/**
|
|
5374
|
-
* Console-based logger implementation.
|
|
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.
|
|
5375
6342
|
*/
|
|
5376
|
-
declare class
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
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>;
|
|
5382
6349
|
}
|
|
5383
6350
|
|
|
5384
6351
|
/**
|
|
5385
|
-
*
|
|
5386
|
-
*
|
|
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.
|
|
5387
6362
|
*/
|
|
5388
|
-
declare class
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
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>;
|
|
5393
6377
|
}
|
|
5394
6378
|
|
|
5395
6379
|
/**
|
|
5396
|
-
*
|
|
5397
|
-
*
|
|
5398
|
-
* When this feature is enabled, events dispatched via `eventBus.dispatch()` will include
|
|
5399
|
-
* the current form value in the `formValue` property. This is useful for event handlers
|
|
5400
|
-
* that need access to the complete form state at the time of the event.
|
|
5401
|
-
*
|
|
5402
|
-
* @example Global opt-in
|
|
5403
|
-
* ```typescript
|
|
5404
|
-
* provideDynamicForm(
|
|
5405
|
-
* ...withMaterialFields(),
|
|
5406
|
-
* withEventFormValue()
|
|
5407
|
-
* )
|
|
5408
|
-
* ```
|
|
5409
|
-
*
|
|
5410
|
-
* @example Consumer usage
|
|
5411
|
-
* ```typescript
|
|
5412
|
-
* eventBus.on<SubmitEvent>('submit').subscribe(event => {
|
|
5413
|
-
* if (hasFormValue(event)) {
|
|
5414
|
-
* console.log('Form value:', event.formValue);
|
|
5415
|
-
* }
|
|
5416
|
-
* });
|
|
5417
|
-
* ```
|
|
5418
|
-
*
|
|
5419
|
-
* @example Per-form disable (when globally enabled)
|
|
5420
|
-
* ```typescript
|
|
5421
|
-
* const config: FormConfig = {
|
|
5422
|
-
* fields: [...],
|
|
5423
|
-
* options: { emitFormValueOnEvents: false }
|
|
5424
|
-
* };
|
|
5425
|
-
* ```
|
|
5426
|
-
*
|
|
5427
|
-
* @remarks
|
|
5428
|
-
* **Precedence rules:**
|
|
5429
|
-
* 1. Per-form `false` - Disables for this form, regardless of global setting
|
|
5430
|
-
* 2. Per-form `true` - Enables for this form, regardless of global setting
|
|
5431
|
-
* 3. Per-form `undefined` - Uses global setting
|
|
5432
|
-
* 4. Global `withEventFormValue()` - Enables globally
|
|
5433
|
-
* 5. No global feature - Disabled globally (default)
|
|
6380
|
+
* Renderer for the universal `component` addon kind.
|
|
5434
6381
|
*
|
|
5435
|
-
*
|
|
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.
|
|
5436
6387
|
*
|
|
5437
|
-
* @
|
|
6388
|
+
* @codeOnly The `component` loader is a function; this kind is dropped in
|
|
6389
|
+
* lenient validation when the config originated from JSON.
|
|
5438
6390
|
*/
|
|
5439
|
-
declare
|
|
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
|
+
}
|
|
5440
6411
|
|
|
5441
6412
|
/**
|
|
5442
|
-
*
|
|
5443
|
-
*
|
|
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.
|
|
5444
6415
|
*
|
|
5445
|
-
*
|
|
5446
|
-
*
|
|
5447
|
-
*
|
|
5448
|
-
*
|
|
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.
|
|
5449
6423
|
*/
|
|
5450
|
-
interface
|
|
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;
|
|
5451
6429
|
/**
|
|
5452
|
-
*
|
|
5453
|
-
*
|
|
5454
|
-
*
|
|
5455
|
-
*
|
|
5456
|
-
*
|
|
5457
|
-
* @remarks
|
|
5458
|
-
* This does NOT affect HiddenField (`type: 'hidden'`) — those fields
|
|
5459
|
-
* store values without UI and their `hidden()` state is always `false`.
|
|
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.
|
|
5460
6434
|
*/
|
|
5461
|
-
readonly
|
|
6435
|
+
readonly fieldDefaultValueGetter?: () => unknown;
|
|
5462
6436
|
/**
|
|
5463
|
-
*
|
|
5464
|
-
*
|
|
5465
|
-
*
|
|
5466
|
-
*
|
|
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.
|
|
5467
6441
|
*/
|
|
5468
|
-
readonly
|
|
6442
|
+
readonly baselineType?: () => string | undefined;
|
|
5469
6443
|
/**
|
|
5470
|
-
*
|
|
6444
|
+
* Logger for warnings about presets that can't be fulfilled in this context.
|
|
5471
6445
|
*
|
|
5472
|
-
*
|
|
5473
|
-
*
|
|
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)`.
|
|
5474
6452
|
*/
|
|
5475
|
-
readonly
|
|
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>;
|
|
5476
6498
|
}
|
|
5477
6499
|
/**
|
|
5478
|
-
*
|
|
5479
|
-
* Produced by the 3-tier resolution: `field ?? form ?? global`.
|
|
6500
|
+
* Injection function for accessing the addon kind registry.
|
|
5480
6501
|
*
|
|
5481
|
-
* @
|
|
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
|
+
* ```
|
|
5482
6510
|
*/
|
|
5483
|
-
|
|
6511
|
+
declare function injectAddonKindRegistry(): AddonKindRegistryRef;
|
|
5484
6512
|
|
|
5485
6513
|
/**
|
|
5486
|
-
*
|
|
6514
|
+
* One entry per field type that declares addon support.
|
|
5487
6515
|
*
|
|
5488
|
-
*
|
|
5489
|
-
* `
|
|
5490
|
-
|
|
5491
|
-
|
|
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`).
|
|
5492
6527
|
*
|
|
5493
|
-
*
|
|
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
|
|
5494
6535
|
* ```typescript
|
|
5495
|
-
*
|
|
5496
|
-
*
|
|
5497
|
-
*
|
|
5498
|
-
*
|
|
6536
|
+
* @Component({ ... })
|
|
6537
|
+
* class FormBuilder {
|
|
6538
|
+
* protected readonly addonable = injectFieldsSupportingAddons();
|
|
6539
|
+
*
|
|
6540
|
+
* constructor() {
|
|
6541
|
+
* // -> [{ name: 'prime-input', slots: ['prefix', 'suffix'], allowedKinds: [...] }, ...]
|
|
6542
|
+
* }
|
|
6543
|
+
* }
|
|
5499
6544
|
* ```
|
|
6545
|
+
*/
|
|
6546
|
+
declare function injectFieldsSupportingAddons(): FieldAddonSupportEntry[];
|
|
6547
|
+
|
|
6548
|
+
/**
|
|
6549
|
+
* Normalises a {@link DynamicValue} into a `Signal<T>`.
|
|
5500
6550
|
*
|
|
5501
|
-
*
|
|
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
|
|
5502
6565
|
* ```typescript
|
|
5503
|
-
*
|
|
5504
|
-
*
|
|
5505
|
-
* withValueExclusionDefaults({ excludeValueIfReadonly: false })
|
|
5506
|
-
* )
|
|
6566
|
+
* const isHidden = resolveDynamicValue(addon().hidden, false);
|
|
6567
|
+
* // template: \@if (isHidden()) { ... }
|
|
5507
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.
|
|
5508
6574
|
*
|
|
5509
|
-
*
|
|
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
|
|
5510
6581
|
* ```typescript
|
|
5511
6582
|
* provideDynamicForm(
|
|
5512
6583
|
* ...withMaterialFields(),
|
|
5513
|
-
*
|
|
5514
|
-
*
|
|
5515
|
-
*
|
|
5516
|
-
*
|
|
5517
|
-
*
|
|
5518
|
-
* )
|
|
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
|
+
* );
|
|
5519
6596
|
* ```
|
|
5520
6597
|
*
|
|
5521
|
-
*
|
|
5522
|
-
*
|
|
5523
|
-
* 1. Per-field `excludeValueIf*` on `FieldDef` — wins for that field
|
|
5524
|
-
* 2. Per-form `excludeValueIf*` on `FormOptions` — wins for all fields in that form
|
|
5525
|
-
* 3. Global `withValueExclusionDefaults()` — baseline default
|
|
5526
|
-
* 4. No global feature — uses token default (all enabled)
|
|
5527
|
-
*
|
|
5528
|
-
* @param config - Partial override of exclusion rules. Unspecified properties default to `true`.
|
|
5529
|
-
* @returns A DynamicFormFeature that configures value exclusion defaults
|
|
5530
|
-
*
|
|
5531
|
-
* @public
|
|
6598
|
+
* Multiple `withCustomAddon(...)` calls are supported; later registrations
|
|
6599
|
+
* for the same kind override earlier ones (with a warning logged).
|
|
5532
6600
|
*/
|
|
5533
|
-
declare function
|
|
6601
|
+
declare function withCustomAddon<T extends BaseAddon>(definition: AddonKindDefinition<T>): DynamicFormFeature<'addons'>;
|
|
5534
6602
|
|
|
5535
6603
|
/**
|
|
5536
|
-
*
|
|
5537
|
-
*
|
|
5538
|
-
* Currently controls whether hidden fields are validated. Resolved through a
|
|
5539
|
-
* tree-aware hierarchy: per-field overrides inherit down to descendants, with
|
|
5540
|
-
* per-form `FormOptions` and global `withValidationExecutionDefaults()` providing
|
|
5541
|
-
* the root inherited value.
|
|
6604
|
+
* Handler signature for user-registered addon actions.
|
|
5542
6605
|
*
|
|
5543
|
-
*
|
|
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.
|
|
5544
6609
|
*/
|
|
5545
|
-
|
|
5546
|
-
/**
|
|
5547
|
-
* Whether validators run while a field is hidden (statically, via `hidden`
|
|
5548
|
-
* logic, or because any ancestor is hidden).
|
|
5549
|
-
*
|
|
5550
|
-
* When `false`, all validators on a hidden field are skipped — the field is
|
|
5551
|
-
* effectively inert until shown. State logic (hidden/disabled/readonly) and
|
|
5552
|
-
* value derivations continue to apply.
|
|
5553
|
-
*/
|
|
5554
|
-
readonly validateWhenHidden?: boolean;
|
|
5555
|
-
}
|
|
6610
|
+
type AddonActionHandler<TValue = unknown> = (ctx: AddonActionContext<TValue>) => void;
|
|
5556
6611
|
/**
|
|
5557
|
-
*
|
|
6612
|
+
* Multi-provider token populated by `provideAddonActions({...})` calls.
|
|
6613
|
+
* `ADDON_ACTION_REGISTRY` aggregates these into a single name → handler map.
|
|
5558
6614
|
*
|
|
5559
|
-
* @
|
|
6615
|
+
* @internal
|
|
5560
6616
|
*/
|
|
5561
|
-
|
|
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>>>;
|
|
5562
6624
|
|
|
5563
6625
|
/**
|
|
5564
|
-
*
|
|
5565
|
-
*
|
|
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.
|
|
5566
6630
|
*
|
|
5567
|
-
*
|
|
5568
|
-
*
|
|
5569
|
-
|
|
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.
|
|
5570
6640
|
*
|
|
5571
|
-
*
|
|
5572
|
-
*
|
|
5573
|
-
*
|
|
5574
|
-
*
|
|
5575
|
-
*
|
|
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:
|
|
5576
6646
|
*
|
|
5577
|
-
* @example Restore legacy validation-on-hidden behavior
|
|
5578
6647
|
* ```typescript
|
|
5579
|
-
*
|
|
5580
|
-
*
|
|
5581
|
-
*
|
|
5582
|
-
* )
|
|
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);
|
|
5583
6660
|
* ```
|
|
5584
6661
|
*
|
|
5585
|
-
*
|
|
5586
|
-
*
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
*
|
|
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.
|
|
5592
6671
|
*
|
|
5593
|
-
* @
|
|
5594
|
-
|
|
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.
|
|
5595
6678
|
*
|
|
5596
|
-
*
|
|
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).
|
|
5597
6682
|
*/
|
|
5598
|
-
|
|
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;
|
|
5599
6735
|
|
|
5600
6736
|
/**
|
|
5601
|
-
*
|
|
6737
|
+
* Walk a field's addons; return the survivors plus a list of warnings for
|
|
6738
|
+
* anything dropped.
|
|
5602
6739
|
*
|
|
5603
|
-
*
|
|
5604
|
-
*
|
|
5605
|
-
*
|
|
5606
|
-
*
|
|
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.
|
|
5607
6813
|
*
|
|
5608
6814
|
* @example
|
|
5609
6815
|
* ```typescript
|
|
5610
|
-
*
|
|
5611
|
-
*
|
|
5612
|
-
*
|
|
5613
|
-
*
|
|
5614
|
-
*
|
|
5615
|
-
*
|
|
5616
|
-
*
|
|
5617
|
-
*
|
|
5618
|
-
*
|
|
5619
|
-
* 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
|
+
* };
|
|
5620
6825
|
* }
|
|
5621
6826
|
* ```
|
|
5622
6827
|
*/
|
|
5623
|
-
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;
|
|
5624
6845
|
|
|
5625
6846
|
interface FieldWithValidation {
|
|
5626
6847
|
readonly required?: boolean;
|
|
@@ -5706,7 +6927,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TN
|
|
|
5706
6927
|
nullable?: TNullable;
|
|
5707
6928
|
}
|
|
5708
6929
|
declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
|
|
5709
|
-
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';
|
|
5710
6931
|
type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
|
|
5711
6932
|
|
|
5712
6933
|
interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends FieldDef<TProps, TMeta>, FieldWithValidation {
|
|
@@ -5719,7 +6940,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta> extends
|
|
|
5719
6940
|
required?: boolean;
|
|
5720
6941
|
}
|
|
5721
6942
|
declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta>;
|
|
5722
|
-
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';
|
|
5723
6944
|
type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
|
|
5724
6945
|
|
|
5725
6946
|
declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>): void;
|
|
@@ -6495,8 +7716,6 @@ declare function applyMetaToElement(element: Element, meta: FieldMeta | undefine
|
|
|
6495
7716
|
* );
|
|
6496
7717
|
* // user.value() keeps the old user data during loading transitions
|
|
6497
7718
|
* ```
|
|
6498
|
-
*
|
|
6499
|
-
* @experimental Uses Angular's experimental resource composition APIs.
|
|
6500
7719
|
*/
|
|
6501
7720
|
declare function withPreviousValue<T>(input: Resource<T>): Resource<T>;
|
|
6502
7721
|
|
|
@@ -6615,6 +7834,12 @@ interface ResolvedArrayItemField {
|
|
|
6615
7834
|
inputs: Signal<Record<string, unknown>>;
|
|
6616
7835
|
/** Whether required mapped inputs are available for safe component instantiation. */
|
|
6617
7836
|
renderReady: Signal<boolean>;
|
|
7837
|
+
/**
|
|
7838
|
+
* Whether the array-item field is currently hidden — mirrors `ResolvedField.hidden`
|
|
7839
|
+
* so the same `@if (!field.hidden())` template gate works for top-level and
|
|
7840
|
+
* array-item fields.
|
|
7841
|
+
*/
|
|
7842
|
+
hidden: Signal<boolean>;
|
|
6618
7843
|
}
|
|
6619
7844
|
/**
|
|
6620
7845
|
* Resolved array item ready for declarative rendering with ngComponentOutlet.
|
|
@@ -6645,7 +7870,9 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
|
|
|
6645
7870
|
protected readonly environmentInjector: EnvironmentInjector;
|
|
6646
7871
|
private readonly eventBus;
|
|
6647
7872
|
private readonly logger;
|
|
6648
|
-
private readonly
|
|
7873
|
+
private readonly arrayItemRegistry;
|
|
7874
|
+
private readonly slot;
|
|
7875
|
+
/** Bound id-generator reference threaded to `resolveArrayItem` so callsites stay terse. */
|
|
6649
7876
|
private readonly generateItemId;
|
|
6650
7877
|
field: _angular_core.InputSignal<ArrayField<readonly ArrayItemDefinition[]>>;
|
|
6651
7878
|
key: _angular_core.InputSignal<string>;
|
|
@@ -6710,9 +7937,7 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
|
|
|
6710
7937
|
private readonly itemTemplates;
|
|
6711
7938
|
private readonly arrayFieldTrees;
|
|
6712
7939
|
private readonly resolvedItemsSignal;
|
|
6713
|
-
private readonly
|
|
6714
|
-
private readonly pendingInitializationCycle;
|
|
6715
|
-
private readonly settledInitializationCycle;
|
|
7940
|
+
private readonly fsm;
|
|
6716
7941
|
/**
|
|
6717
7942
|
* Map of item IDs to their current positions. O(1) lookup vs O(n) indexOf().
|
|
6718
7943
|
* Used by child linkedSignals to reactively track their position in the array.
|
|
@@ -6832,71 +8057,6 @@ declare class ContainerFieldComponent {
|
|
|
6832
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>;
|
|
6833
8058
|
}
|
|
6834
8059
|
|
|
6835
|
-
/**
|
|
6836
|
-
* Type representing a FieldTree for an array.
|
|
6837
|
-
* Extends FieldTree to add numeric indexing for accessing item FieldTrees.
|
|
6838
|
-
*/
|
|
6839
|
-
type ArrayFieldTree<T> = FieldTree<T[]> & {
|
|
6840
|
-
readonly [index: number]: FieldTree<T> | undefined;
|
|
6841
|
-
};
|
|
6842
|
-
/**
|
|
6843
|
-
* Get the length of an array FieldTree.
|
|
6844
|
-
*/
|
|
6845
|
-
declare function getArrayLength<T>(arrayFieldTree: ArrayFieldTree<T>): number;
|
|
6846
|
-
/**
|
|
6847
|
-
* Read-only view of a single field's observable state.
|
|
6848
|
-
*
|
|
6849
|
-
* Whitelisted read signals copied from Angular Signal Forms' `FieldState` so wrappers
|
|
6850
|
-
* can observe a field without being able to mutate it. New write-capable members
|
|
6851
|
-
* added in future Angular versions are excluded by default (the `Pick` list stays
|
|
6852
|
-
* the source of truth).
|
|
6853
|
-
*
|
|
6854
|
-
* `value` is narrowed from `WritableSignal<TValue>` to `Signal<TValue>` so consumers
|
|
6855
|
-
* cannot write through it.
|
|
6856
|
-
*/
|
|
6857
|
-
interface ReadonlyFieldTree<TValue = unknown> {
|
|
6858
|
-
readonly value: Signal<TValue>;
|
|
6859
|
-
readonly valid: Signal<boolean>;
|
|
6860
|
-
readonly invalid: Signal<boolean>;
|
|
6861
|
-
readonly touched: Signal<boolean>;
|
|
6862
|
-
readonly dirty: Signal<boolean>;
|
|
6863
|
-
readonly required: Signal<boolean>;
|
|
6864
|
-
readonly disabled: Signal<boolean>;
|
|
6865
|
-
readonly hidden: Signal<boolean>;
|
|
6866
|
-
readonly errors: Signal<readonly unknown[]>;
|
|
6867
|
-
}
|
|
6868
|
-
/**
|
|
6869
|
-
* Build a `ReadonlyFieldTree` by extracting the whitelisted read signals from a
|
|
6870
|
-
* Signal Forms `FieldTree`. Returns a fresh plain object — no casting, no proxying,
|
|
6871
|
-
* so consumers only see the narrow surface and Angular's `WritableSignal` capability
|
|
6872
|
-
* on `value` is hidden.
|
|
6873
|
-
*/
|
|
6874
|
-
declare function toReadonlyFieldTree<TValue>(field: FieldTree<TValue>): ReadonlyFieldTree<TValue>;
|
|
6875
|
-
|
|
6876
|
-
/**
|
|
6877
|
-
* Input shape passed to a wrapper component via the `fieldInputs` input.
|
|
6878
|
-
*
|
|
6879
|
-
* Carries the wrapped field's mapper outputs plus an optional `field`
|
|
6880
|
-
* read-only view. Wrappers reached through a container path (which has
|
|
6881
|
-
* no FieldTree of its own) receive `fieldInputs === undefined`, so any
|
|
6882
|
-
* wrapper that reads `fieldInputs.field` must guard for it.
|
|
6883
|
-
*
|
|
6884
|
-
* Mappers must emit new rawInputs objects per tick rather than mutating
|
|
6885
|
-
* the previous one — this bag is a shallow spread, so mutations to shared
|
|
6886
|
-
* nested values leak to downstream wrappers.
|
|
6887
|
-
*/
|
|
6888
|
-
interface WrapperFieldInputs {
|
|
6889
|
-
readonly key: string;
|
|
6890
|
-
readonly label?: string;
|
|
6891
|
-
readonly placeholder?: string;
|
|
6892
|
-
readonly className?: string;
|
|
6893
|
-
readonly props?: Record<string, unknown>;
|
|
6894
|
-
readonly validationMessages?: Record<string, string>;
|
|
6895
|
-
readonly defaultValidationMessages?: Record<string, string>;
|
|
6896
|
-
readonly field?: ReadonlyFieldTree;
|
|
6897
|
-
readonly [key: string]: unknown;
|
|
6898
|
-
}
|
|
6899
|
-
|
|
6900
8060
|
/**
|
|
6901
8061
|
* Native JavaScript utility functions to replace lodash-es
|
|
6902
8062
|
* These are simple implementations for common object manipulation patterns
|
|
@@ -6975,5 +8135,5 @@ declare class RootFormRegistryService {
|
|
|
6975
8135
|
*/
|
|
6976
8136
|
declare const INITIALIZATION_TIMEOUT_MS: InjectionToken<number>;
|
|
6977
8137
|
|
|
6978
|
-
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 };
|
|
6979
|
-
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 };
|