@ng-forge/dynamic-forms 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/README.md +3 -2
  2. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +23 -18
  3. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  4. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CMSmISSp.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-DHxoGouT.mjs} +248 -61
  5. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-DHxoGouT.mjs.map +1 -0
  6. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs → ng-forge-dynamic-forms-page-field.component-Bt6blv9I.mjs} +13 -7
  7. package/fesm2022/ng-forge-dynamic-forms-page-field.component-Bt6blv9I.mjs.map +1 -0
  8. package/fesm2022/{ng-forge-dynamic-forms-testing-test-button.harness-0iXHLraC.mjs → ng-forge-dynamic-forms-testing-test-button.harness-C79EBW0o.mjs} +4 -4
  9. package/fesm2022/{ng-forge-dynamic-forms-testing-test-button.harness-0iXHLraC.mjs.map → ng-forge-dynamic-forms-testing-test-button.harness-C79EBW0o.mjs.map} +1 -1
  10. package/fesm2022/ng-forge-dynamic-forms-testing.mjs +32 -30
  11. package/fesm2022/ng-forge-dynamic-forms-testing.mjs.map +1 -1
  12. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-CDxka2KD.mjs → ng-forge-dynamic-forms-text-field.component-DKCx7Z_U.mjs} +5 -5
  13. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-CDxka2KD.mjs.map → ng-forge-dynamic-forms-text-field.component-DKCx7Z_U.mjs.map} +1 -1
  14. package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
  15. package/package.json +9 -9
  16. package/types/ng-forge-dynamic-forms-integration.d.ts +58 -18
  17. package/types/ng-forge-dynamic-forms-testing.d.ts +7 -1
  18. package/types/ng-forge-dynamic-forms.d.ts +375 -167
  19. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CMSmISSp.mjs.map +0 -1
  20. package/fesm2022/ng-forge-dynamic-forms-page-field.component-rdGD8A9d.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { Provider, Injector, WritableSignal, Type } from '@angular/core';
3
3
  import { Mock } from 'vitest';
4
- import { Logger, FieldDef, FormConfig, DynamicForm, ValidationMessages, FieldSignalContext, RegisteredFieldTypes } from '@ng-forge/dynamic-forms';
4
+ import { Logger, FieldDef, FormConfig, DynamicForm, ValidationMessages, FormOptions, FieldSignalContext, RegisteredFieldTypes } from '@ng-forge/dynamic-forms';
5
5
  import { ComponentFixture } from '@angular/core/testing';
6
6
  import { ValidationError, form, FieldTree } from '@angular/forms/signals';
7
7
  import { Observable } from 'rxjs';
@@ -174,8 +174,14 @@ interface TestFieldContextConfig<TModel extends Record<string, unknown> = Record
174
174
  defaultValues?: () => TModel;
175
175
  /**
176
176
  * Default validation messages for the form.
177
+ * Provided via DEFAULT_VALIDATION_MESSAGES injection token.
177
178
  */
178
179
  defaultValidationMessages?: ValidationMessages;
180
+ /**
181
+ * Form options for button disabled state defaults.
182
+ * Provided via FORM_OPTIONS injection token.
183
+ */
184
+ formOptions?: FormOptions;
179
185
  /**
180
186
  * Custom value signal. If provided, this will be used instead of creating a new one.
181
187
  */
@@ -2,10 +2,10 @@ import * as rxjs from 'rxjs';
2
2
  import { Observable } from 'rxjs';
3
3
  import * as _angular_forms from '@angular/forms';
4
4
  import * as _angular_forms_signals from '@angular/forms/signals';
5
- import { ValidationError as ValidationError$1, FieldContext, TreeValidationResult, FieldTree, Schema, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
5
+ import { FieldTree, FieldContext, ValidationError as ValidationError$1, TreeValidationResult, Schema, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
6
6
  import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
7
7
  import * as _angular_core from '@angular/core';
8
- import { Signal, ResourceRef, Injector, WritableSignal, InjectionToken, Type, OnDestroy, InputSignal, Provider, EnvironmentProviders, PipeTransform } from '@angular/core';
8
+ import { Signal, Injector, WritableSignal, InjectionToken, Type, ResourceRef, OnDestroy, InputSignal, Provider, EnvironmentProviders, PipeTransform } from '@angular/core';
9
9
 
10
10
  type WithInputSignals<T> = {
11
11
  readonly [K in keyof T]-?: Signal<T[K]>;
@@ -299,19 +299,151 @@ type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
299
299
  type FieldComponent<T extends FieldDef<unknown, FieldMeta>> = Prettify<WithInputSignals<Pick<T, IncludedKeys>>>;
300
300
 
301
301
  /**
302
- * Re-export Angular's ValidationError for consistency
303
- * This replaces our custom ValidationError interface
302
+ * Field signal context - the "nervous system" of the dynamic form.
303
+ * Provided via FIELD_SIGNAL_CONTEXT injection token.
304
+ *
305
+ * Gives mappers and components access to form state, values, and configuration.
306
+ * Container fields (Group, Array) create scoped contexts with nested forms.
307
+ *
308
+ * The `form` property uses Angular's FieldTree which includes Subfields<TModel>
309
+ * for type-safe child field access via bracket notation when TModel is a Record.
310
+ *
311
+ * Note: Form-level configuration (defaultValidationMessages, formOptions, defaultProps)
312
+ * is provided via dedicated injection tokens (DEFAULT_VALIDATION_MESSAGES, FORM_OPTIONS,
313
+ * DEFAULT_PROPS) at the DynamicForm level and inherited by all children.
304
314
  */
305
- type ValidationError = ValidationError$1;
306
- interface ValidationMessages {
307
- required?: DynamicText;
308
- email?: DynamicText;
309
- min?: DynamicText;
310
- max?: DynamicText;
311
- minLength?: DynamicText;
312
- maxLength?: DynamicText;
313
- pattern?: DynamicText;
314
- [key: string]: DynamicText | undefined;
315
+ interface FieldSignalContext<TModel extends Record<string, unknown> = Record<string, unknown>> {
316
+ injector: Injector;
317
+ value: WritableSignal<Partial<TModel> | undefined>;
318
+ defaultValues: () => TModel;
319
+ form: FieldTree<TModel>;
320
+ /** Current page validity signal for paged forms. Used by next button to determine disabled state. */
321
+ currentPageValid?: Signal<boolean>;
322
+ }
323
+ /**
324
+ * Array context for fields rendered within arrays.
325
+ * Provides position and parent array information for components inside array fields.
326
+ */
327
+ interface ArrayContext {
328
+ /** The key of the parent array field. */
329
+ arrayKey: string;
330
+ /**
331
+ * The index of this item within the array.
332
+ * Uses linkedSignal to automatically update when items are added/removed,
333
+ * allowing index-dependent logic to react without component recreation.
334
+ */
335
+ index: Signal<number>;
336
+ /** The current form value for token resolution. */
337
+ formValue: unknown;
338
+ /** The array field definition. */
339
+ field: FieldDef<unknown>;
340
+ }
341
+ /**
342
+ * Mapper function that converts a field definition to component inputs.
343
+ *
344
+ * Mappers run within an injection context and can inject FIELD_SIGNAL_CONTEXT.
345
+ * Returns a Signal to enable reactive updates when dependencies change.
346
+ */
347
+ type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
348
+
349
+ /**
350
+ * Defines how a field type handles form values and data collection.
351
+ *
352
+ * - 'include': Field contributes to form values (default for input fields)
353
+ * - 'exclude': Field is excluded from form values (for display/layout fields)
354
+ * - 'flatten': Field's children are flattened to parent level (for container fields)
355
+ */
356
+ type ValueHandlingMode = 'include' | 'exclude' | 'flatten';
357
+ /**
358
+ * Configuration interface for registering custom field types.
359
+ *
360
+ * Defines how a field type should be handled by the dynamic form system,
361
+ * including its component loading strategy and field-to-component mapping logic.
362
+ * Supports both eager-loaded and lazy-loaded components.
363
+ *
364
+ * @typeParam T - The field definition type this field type handles
365
+ *
366
+ * @example
367
+ * ```typescript
368
+ * const CustomInputType: FieldTypeDefinition<InputFieldDef> = {
369
+ * name: 'custom-input',
370
+ * loadComponent: () => import('./custom-input.component').then(m => m.CustomInputComponent),
371
+ * mapper: customInputMapper
372
+ * };
373
+ *
374
+ * // Register with providers
375
+ * provideDynamicForm(CustomInputType)
376
+ * ```
377
+ */
378
+ interface FieldTypeDefinition<T extends FieldDef<any> = any> {
379
+ /** Unique identifier for the field type */
380
+ name: string;
381
+ /** Field definition type marker (internal use) */
382
+ _fieldDef?: T;
383
+ /**
384
+ * Function to load the component (supports lazy loading).
385
+ * Returns a Promise that resolves to the component class or module with default export.
386
+ *
387
+ * Optional - omit for componentless fields (e.g., hidden fields) that only
388
+ * contribute to form values without rendering any UI.
389
+ */
390
+ loadComponent?: () => Promise<any>;
391
+ /**
392
+ * Mapper function that converts field definition to component bindings.
393
+ *
394
+ * Optional - omit for componentless fields (like hidden fields) that don't need
395
+ * input mapping. When omitted for componentless fields (no loadComponent), an empty
396
+ * signal is returned. When omitted for regular fields with a component, falls back
397
+ * to baseFieldMapper.
398
+ */
399
+ mapper?: MapperFn<T>;
400
+ /** How this field type handles form values and data collection (defaults to 'include') */
401
+ valueHandling?: ValueHandlingMode;
402
+ /**
403
+ * List of prop keys to forward to the meta object.
404
+ *
405
+ * Some props (like `type` for inputs, `rows`/`cols` for textareas) are actually
406
+ * native HTML attributes. Specifying them here causes them to be merged into
407
+ * the meta object before being passed to the component, ensuring they're applied
408
+ * via the meta tracking mechanism.
409
+ *
410
+ * Note: Meta values always take precedence over forwarded props.
411
+ *
412
+ * @example
413
+ * ```typescript
414
+ * {
415
+ * name: 'input',
416
+ * propsToMeta: ['type'],
417
+ * // ...
418
+ * }
419
+ * ```
420
+ */
421
+ propsToMeta?: string[];
422
+ }
423
+ /**
424
+ * Injection token for the global field type registry.
425
+ *
426
+ * Provides access to the map of registered field types throughout the application.
427
+ * The registry is populated by the provideDynamicForm function and used by
428
+ * field rendering components to resolve field types to their implementations.
429
+ *
430
+ * @example
431
+ * ```typescript
432
+ * constructor(@Inject(FIELD_REGISTRY) private registry: Map<string, FieldTypeDefinition>) {
433
+ * const inputType = registry.get('input');
434
+ * }
435
+ * ```
436
+ */
437
+ declare const FIELD_REGISTRY: InjectionToken<Map<string, FieldTypeDefinition<any>>>;
438
+
439
+ /**
440
+ * Resolved field ready for rendering with ngComponentOutlet.
441
+ */
442
+ interface ResolvedField {
443
+ key: string;
444
+ component: Type<unknown>;
445
+ injector: Injector;
446
+ inputs: Signal<Record<string, unknown>>;
315
447
  }
316
448
 
317
449
  /**
@@ -1393,6 +1525,22 @@ interface EvaluationContext<TValue = unknown> {
1393
1525
  */
1394
1526
  type CustomFunction = (context: EvaluationContext) => unknown;
1395
1527
 
1528
+ /**
1529
+ * Re-export Angular's ValidationError for consistency
1530
+ * This replaces our custom ValidationError interface
1531
+ */
1532
+ type ValidationError = ValidationError$1;
1533
+ interface ValidationMessages {
1534
+ required?: DynamicText;
1535
+ email?: DynamicText;
1536
+ min?: DynamicText;
1537
+ max?: DynamicText;
1538
+ minLength?: DynamicText;
1539
+ maxLength?: DynamicText;
1540
+ pattern?: DynamicText;
1541
+ [key: string]: DynamicText | undefined;
1542
+ }
1543
+
1396
1544
  /**
1397
1545
  * The result type for submission actions.
1398
1546
  * Can be either a Promise or an Observable.
@@ -1545,10 +1693,11 @@ interface SubmissionConfig<TValue = unknown> {
1545
1693
  *
1546
1694
  * @typeParam TFields - Array of registered field types available for this form
1547
1695
  * @typeParam TValue - The strongly-typed interface for form values
1696
+ * @typeParam TProps - The type for form-level default props (library-specific)
1548
1697
  *
1549
1698
  * @public
1550
1699
  */
1551
- interface FormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = RegisteredFieldTypes[], TValue = InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>> {
1700
+ interface FormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = RegisteredFieldTypes[], TValue = InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>, TProps extends object = Record<string, unknown>> {
1552
1701
  /**
1553
1702
  * Array of field definitions that define the form structure.
1554
1703
  *
@@ -1659,6 +1808,32 @@ interface FormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = Reg
1659
1808
  * ```
1660
1809
  */
1661
1810
  submission?: SubmissionConfig<TValue>;
1811
+ /**
1812
+ * Default props applied to all fields in the form.
1813
+ *
1814
+ * These props serve as defaults that can be overridden at the field level.
1815
+ * Useful for setting consistent styling across the entire form (e.g., appearance,
1816
+ * size, or other UI library-specific props).
1817
+ *
1818
+ * The cascade order is: Library config → Form defaultProps → Field props
1819
+ * Each level can override the previous one.
1820
+ *
1821
+ * @example
1822
+ * ```typescript
1823
+ * // Material example
1824
+ * const config: MatFormConfig = {
1825
+ * defaultProps: {
1826
+ * appearance: 'outline',
1827
+ * subscriptSizing: 'dynamic',
1828
+ * },
1829
+ * fields: [
1830
+ * { type: 'input', key: 'name', label: 'Name' }, // Uses defaultProps
1831
+ * { type: 'input', key: 'email', props: { appearance: 'fill' } }, // Override
1832
+ * ],
1833
+ * };
1834
+ * ```
1835
+ */
1836
+ defaultProps?: TProps;
1662
1837
  }
1663
1838
  /**
1664
1839
  * Signal forms adapter configuration for advanced form behavior.
@@ -2005,153 +2180,6 @@ interface NextButtonOptions {
2005
2180
  disableWhileSubmitting?: boolean;
2006
2181
  }
2007
2182
 
2008
- /**
2009
- * Field signal context - the "nervous system" of the dynamic form.
2010
- * Provided via FIELD_SIGNAL_CONTEXT injection token.
2011
- *
2012
- * Gives mappers and components access to form state, values, and configuration.
2013
- * Container fields (Group, Array) create scoped contexts with nested forms.
2014
- *
2015
- * The `form` property uses Angular's FieldTree which includes Subfields<TModel>
2016
- * for type-safe child field access via bracket notation when TModel is a Record.
2017
- */
2018
- interface FieldSignalContext<TModel extends Record<string, unknown> = Record<string, unknown>> {
2019
- injector: Injector;
2020
- value: WritableSignal<Partial<TModel> | undefined>;
2021
- defaultValues: () => TModel;
2022
- form: FieldTree<TModel>;
2023
- defaultValidationMessages?: ValidationMessages;
2024
- /** Form-level options used by button mappers to determine disabled state defaults. */
2025
- formOptions?: FormOptions;
2026
- /** Current page validity signal for paged forms. Used by next button to determine disabled state. */
2027
- currentPageValid?: Signal<boolean>;
2028
- }
2029
- /**
2030
- * Array context for fields rendered within arrays.
2031
- * Provides position and parent array information for components inside array fields.
2032
- */
2033
- interface ArrayContext {
2034
- /** The key of the parent array field. */
2035
- arrayKey: string;
2036
- /**
2037
- * The index of this item within the array.
2038
- * Uses linkedSignal to automatically update when items are added/removed,
2039
- * allowing index-dependent logic to react without component recreation.
2040
- */
2041
- index: Signal<number>;
2042
- /** The current form value for token resolution. */
2043
- formValue: unknown;
2044
- /** The array field definition. */
2045
- field: FieldDef<unknown>;
2046
- }
2047
- /**
2048
- * Mapper function that converts a field definition to component inputs.
2049
- *
2050
- * Mappers run within an injection context and can inject FIELD_SIGNAL_CONTEXT.
2051
- * Returns a Signal to enable reactive updates when dependencies change.
2052
- */
2053
- type MapperFn<T extends FieldDef<unknown>> = (input: T) => Signal<Record<string, unknown>>;
2054
-
2055
- /**
2056
- * Defines how a field type handles form values and data collection.
2057
- *
2058
- * - 'include': Field contributes to form values (default for input fields)
2059
- * - 'exclude': Field is excluded from form values (for display/layout fields)
2060
- * - 'flatten': Field's children are flattened to parent level (for container fields)
2061
- */
2062
- type ValueHandlingMode = 'include' | 'exclude' | 'flatten';
2063
- /**
2064
- * Configuration interface for registering custom field types.
2065
- *
2066
- * Defines how a field type should be handled by the dynamic form system,
2067
- * including its component loading strategy and field-to-component mapping logic.
2068
- * Supports both eager-loaded and lazy-loaded components.
2069
- *
2070
- * @typeParam T - The field definition type this field type handles
2071
- *
2072
- * @example
2073
- * ```typescript
2074
- * const CustomInputType: FieldTypeDefinition<InputFieldDef> = {
2075
- * name: 'custom-input',
2076
- * loadComponent: () => import('./custom-input.component').then(m => m.CustomInputComponent),
2077
- * mapper: customInputMapper
2078
- * };
2079
- *
2080
- * // Register with providers
2081
- * provideDynamicForm(CustomInputType)
2082
- * ```
2083
- */
2084
- interface FieldTypeDefinition<T extends FieldDef<any> = any> {
2085
- /** Unique identifier for the field type */
2086
- name: string;
2087
- /** Field definition type marker (internal use) */
2088
- _fieldDef?: T;
2089
- /**
2090
- * Function to load the component (supports lazy loading).
2091
- * Returns a Promise that resolves to the component class or module with default export.
2092
- *
2093
- * Optional - omit for componentless fields (e.g., hidden fields) that only
2094
- * contribute to form values without rendering any UI.
2095
- */
2096
- loadComponent?: () => Promise<any>;
2097
- /**
2098
- * Mapper function that converts field definition to component bindings.
2099
- *
2100
- * Optional - omit for componentless fields (like hidden fields) that don't need
2101
- * input mapping. When omitted for componentless fields (no loadComponent), an empty
2102
- * signal is returned. When omitted for regular fields with a component, falls back
2103
- * to baseFieldMapper.
2104
- */
2105
- mapper?: MapperFn<T>;
2106
- /** How this field type handles form values and data collection (defaults to 'include') */
2107
- valueHandling?: ValueHandlingMode;
2108
- /**
2109
- * List of prop keys to forward to the meta object.
2110
- *
2111
- * Some props (like `type` for inputs, `rows`/`cols` for textareas) are actually
2112
- * native HTML attributes. Specifying them here causes them to be merged into
2113
- * the meta object before being passed to the component, ensuring they're applied
2114
- * via the meta tracking mechanism.
2115
- *
2116
- * Note: Meta values always take precedence over forwarded props.
2117
- *
2118
- * @example
2119
- * ```typescript
2120
- * {
2121
- * name: 'input',
2122
- * propsToMeta: ['type'],
2123
- * // ...
2124
- * }
2125
- * ```
2126
- */
2127
- propsToMeta?: string[];
2128
- }
2129
- /**
2130
- * Injection token for the global field type registry.
2131
- *
2132
- * Provides access to the map of registered field types throughout the application.
2133
- * The registry is populated by the provideDynamicForm function and used by
2134
- * field rendering components to resolve field types to their implementations.
2135
- *
2136
- * @example
2137
- * ```typescript
2138
- * constructor(@Inject(FIELD_REGISTRY) private registry: Map<string, FieldTypeDefinition>) {
2139
- * const inputType = registry.get('input');
2140
- * }
2141
- * ```
2142
- */
2143
- declare const FIELD_REGISTRY: InjectionToken<Map<string, FieldTypeDefinition<any>>>;
2144
-
2145
- /**
2146
- * Resolved field ready for rendering with ngComponentOutlet.
2147
- */
2148
- interface ResolvedField {
2149
- key: string;
2150
- component: Type<unknown>;
2151
- injector: Injector;
2152
- inputs: Signal<Record<string, unknown>>;
2153
- }
2154
-
2155
2183
  /**
2156
2184
  * Form mode enumeration distinguishing between paged and non-paged forms
2157
2185
  */
@@ -2389,6 +2417,10 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
2389
2417
  nextButton?: _ng_forge_dynamic_forms.NextButtonOptions;
2390
2418
  }>;
2391
2419
  readonly fieldSignalContext: Signal<FieldSignalContext<TModel>>;
2420
+ /**
2421
+ * Injector for field components with FIELD_SIGNAL_CONTEXT.
2422
+ * Other form-level tokens (DEFAULT_PROPS, etc.) are provided at component level.
2423
+ */
2392
2424
  private readonly fieldInjector;
2393
2425
  readonly defaultValues: _angular_core.WritableSignal<TModel>;
2394
2426
  private readonly entity;
@@ -2731,6 +2763,82 @@ declare const FIELD_SIGNAL_CONTEXT: InjectionToken<FieldSignalContext<Record<str
2731
2763
  * ```
2732
2764
  */
2733
2765
  declare const ARRAY_CONTEXT: InjectionToken<ArrayContext>;
2766
+ /**
2767
+ * Injection token for form-level default props.
2768
+ *
2769
+ * Default props are form-wide property defaults that are merged with field-level props.
2770
+ * Field props take precedence over default props.
2771
+ *
2772
+ * Unlike FIELD_SIGNAL_CONTEXT which is re-provided by container components (Group, Array),
2773
+ * DEFAULT_PROPS is provided ONCE at the DynamicForm level and inherited by all children
2774
+ * via Angular's hierarchical injector.
2775
+ *
2776
+ * The token provides a Signal to enable reactivity - when config changes, mappers
2777
+ * that read the signal inside their computed() will automatically update.
2778
+ *
2779
+ * @example
2780
+ * ```typescript
2781
+ * // In a mapper function
2782
+ * const defaultPropsSignal = inject(DEFAULT_PROPS);
2783
+ *
2784
+ * return computed(() => {
2785
+ * const defaultProps = defaultPropsSignal?.(); // Read inside computed for reactivity
2786
+ * const baseInputs = buildBaseInputs(fieldDef, defaultProps);
2787
+ * // ...
2788
+ * });
2789
+ * ```
2790
+ */
2791
+ declare const DEFAULT_PROPS: InjectionToken<Signal<Record<string, unknown> | undefined>>;
2792
+ /**
2793
+ * Injection token for form-level default validation messages.
2794
+ *
2795
+ * Default validation messages act as fallbacks when fields have validation
2796
+ * errors but no field-level `validationMessages` defined.
2797
+ *
2798
+ * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
2799
+ * and inherited by all children via Angular's hierarchical injector.
2800
+ *
2801
+ * The token provides a Signal to enable reactivity - when config changes, mappers
2802
+ * that read the signal inside their computed() will automatically update.
2803
+ *
2804
+ * @example
2805
+ * ```typescript
2806
+ * // In a mapper or component
2807
+ * const defaultMessagesSignal = inject(DEFAULT_VALIDATION_MESSAGES);
2808
+ *
2809
+ * return computed(() => {
2810
+ * const defaultMessages = defaultMessagesSignal?.(); // Read inside computed for reactivity
2811
+ * const message = defaultMessages?.required ?? 'Field is required';
2812
+ * // ...
2813
+ * });
2814
+ * ```
2815
+ */
2816
+ declare const DEFAULT_VALIDATION_MESSAGES: InjectionToken<Signal<ValidationMessages | undefined>>;
2817
+ /**
2818
+ * Injection token for form-level options.
2819
+ *
2820
+ * Form options control form-wide behavior including button disabled states.
2821
+ * Used by button mappers to determine default disabled behavior.
2822
+ *
2823
+ * Like DEFAULT_PROPS, this token is provided ONCE at the DynamicForm level
2824
+ * and inherited by all children via Angular's hierarchical injector.
2825
+ *
2826
+ * The token provides a Signal to enable reactivity - when config changes, mappers
2827
+ * that read the signal inside their computed() will automatically update.
2828
+ *
2829
+ * @example
2830
+ * ```typescript
2831
+ * // In a button mapper
2832
+ * const formOptionsSignal = inject(FORM_OPTIONS);
2833
+ *
2834
+ * return computed(() => {
2835
+ * const formOptions = formOptionsSignal?.(); // Read inside computed for reactivity
2836
+ * const disableWhenInvalid = formOptions?.submitButton?.disableWhenInvalid ?? true;
2837
+ * // ...
2838
+ * });
2839
+ * ```
2840
+ */
2841
+ declare const FORM_OPTIONS: InjectionToken<Signal<FormOptions | undefined>>;
2734
2842
 
2735
2843
  /**
2736
2844
  * Context for resolving button disabled state.
@@ -3079,9 +3187,10 @@ declare class DynamicFormError extends Error {
3079
3187
  * Used by mappers to build the inputs record.
3080
3188
  *
3081
3189
  * @param fieldDef The field definition to extract properties from
3190
+ * @param defaultProps Optional form-level default props to merge with field props
3082
3191
  * @returns Record of input names to values
3083
3192
  */
3084
- declare function buildBaseInputs(fieldDef: FieldDef<unknown>): Record<string, unknown>;
3193
+ declare function buildBaseInputs(fieldDef: FieldDef<unknown>, defaultProps?: Record<string, unknown>): Record<string, unknown>;
3085
3194
  /**
3086
3195
  * Base field mapper that extracts common field properties into component inputs.
3087
3196
  *
@@ -3239,6 +3348,8 @@ declare class RowFieldComponent {
3239
3348
  private readonly logger;
3240
3349
  field: _angular_core.InputSignal<RowField<readonly _ng_forge_dynamic_forms.RowAllowedChildren[]>>;
3241
3350
  key: _angular_core.InputSignal<string>;
3351
+ className: _angular_core.InputSignal<string | undefined>;
3352
+ readonly hostClasses: _angular_core.Signal<string>;
3242
3353
  readonly disabled: _angular_core.Signal<boolean>;
3243
3354
  private readonly rawFieldRegistry;
3244
3355
  private readonly fieldsSource;
@@ -3246,7 +3357,7 @@ declare class RowFieldComponent {
3246
3357
  constructor();
3247
3358
  private setupEffects;
3248
3359
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<RowFieldComponent, never>;
3249
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<RowFieldComponent, "div[row-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
3360
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<RowFieldComponent, "div[row-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3250
3361
  }
3251
3362
 
3252
3363
  /**
@@ -3268,6 +3379,8 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
3268
3379
  private readonly memoizedDefaultValues;
3269
3380
  field: _angular_core.InputSignal<GroupField<readonly _ng_forge_dynamic_forms.GroupAllowedChildren[]>>;
3270
3381
  key: _angular_core.InputSignal<string>;
3382
+ className: _angular_core.InputSignal<string | undefined>;
3383
+ readonly hostClasses: _angular_core.Signal<string>;
3271
3384
  private readonly rawFieldRegistry;
3272
3385
  private readonly formSetup;
3273
3386
  readonly defaultValues: _angular_core.WritableSignal<Record<string, unknown>>;
@@ -3290,7 +3403,7 @@ declare class GroupFieldComponent<TModel extends Record<string, unknown> = Recor
3290
3403
  constructor();
3291
3404
  private setupEffects;
3292
3405
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<GroupFieldComponent<any>, never>;
3293
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<GroupFieldComponent<any>, "fieldset[group-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; }, { "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; }, never, never, true, never>;
3406
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<GroupFieldComponent<any>, "fieldset[group-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, { "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; }, never, never, true, never>;
3294
3407
  }
3295
3408
 
3296
3409
  /**
@@ -3325,6 +3438,8 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
3325
3438
  private readonly logger;
3326
3439
  field: _angular_core.InputSignal<ArrayField<readonly _ng_forge_dynamic_forms.ArrayAllowedChildren[]>>;
3327
3440
  key: _angular_core.InputSignal<string>;
3441
+ className: _angular_core.InputSignal<string | undefined>;
3442
+ readonly hostClasses: _angular_core.Signal<string>;
3328
3443
  private readonly rawFieldRegistry;
3329
3444
  private readonly fieldTemplate;
3330
3445
  private readonly arrayFieldTrees;
@@ -3342,7 +3457,7 @@ declare class ArrayFieldComponent<TModel extends Record<string, unknown> = Recor
3342
3457
  private addItem;
3343
3458
  private removeItem;
3344
3459
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ArrayFieldComponent<any>, never>;
3345
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArrayFieldComponent<any>, "array-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
3460
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ArrayFieldComponent<any>, "array-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3346
3461
  }
3347
3462
 
3348
3463
  /**
@@ -3390,5 +3505,98 @@ declare function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): O
3390
3505
  */
3391
3506
  declare function interpolateParams(message: string, error: ValidationError): string;
3392
3507
 
3393
- export { ARRAY_CONTEXT, AddArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FormClearEvent, FormResetEvent, GroupFieldComponent, NextPageEvent, NoopLogger, PageChangeEvent, PreviousPageEvent, RemoveArrayItemEvent, RowFieldComponent, SubmitEvent, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, baseFieldMapper, buildBaseInputs, dynamicTextToObservable, getArrayLength, groupFieldMapper, interpolateParams, isArrayField, isCheckedField, isContainerField, isDisplayOnlyField, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isValueBearingField, isValueField, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, withLoggerConfig };
3508
+ /**
3509
+ * Registry service that maintains references to root form fields.
3510
+ * This provides a type-safe way to access root form values from child fields
3511
+ * without relying on internal Angular Forms API navigation.
3512
+ *
3513
+ * This service should be provided at the component level to ensure proper
3514
+ * isolation between different form instances.
3515
+ *
3516
+ * IMPORTANT: This registry supports two registration patterns:
3517
+ * 1. registerFormValueSignal() - Register the SOURCE signal BEFORE form creation
3518
+ * This allows logic functions to read form values during schema evaluation.
3519
+ * 2. registerRootForm() - Register the FieldTree AFTER form creation
3520
+ * This provides access to the full form structure.
3521
+ *
3522
+ * For cross-field logic to work correctly, registerFormValueSignal() should be
3523
+ * called BEFORE the form() call.
3524
+ */
3525
+ declare class RootFormRegistryService {
3526
+ private readonly _rootForms;
3527
+ private readonly _formValueSignals;
3528
+ private readonly formContexts;
3529
+ /**
3530
+ * Registers the form value signal BEFORE form creation.
3531
+ *
3532
+ * This is the recommended pattern for cross-field logic:
3533
+ * 1. Create the formValue signal
3534
+ * 2. Register it with registerFormValueSignal()
3535
+ * 3. Create the form with form(formValue, schema(...))
3536
+ *
3537
+ * This allows logic functions to access form values during schema evaluation.
3538
+ */
3539
+ registerFormValueSignal(valueSignal: Signal<Record<string, unknown>>, formId?: string): void;
3540
+ /**
3541
+ * Gets the registered form value signal by id.
3542
+ *
3543
+ * This allows direct access to the source signal, which can be read
3544
+ * reactively to get form values.
3545
+ */
3546
+ getFormValueSignal(formId?: string): Signal<Record<string, unknown>> | undefined;
3547
+ /**
3548
+ * Registers a root form field with an optional identifier.
3549
+ * If no id is provided, uses 'default' as the key.
3550
+ *
3551
+ * This triggers reactive updates for any computed signals that
3552
+ * depend on getRootForm().
3553
+ */
3554
+ registerRootForm(rootField: FieldTree<unknown>, formId?: string): void;
3555
+ /**
3556
+ * Gets a registered root form field by id.
3557
+ *
3558
+ * IMPORTANT: This method reads from a signal, which means any computed
3559
+ * signal that calls this method will automatically re-evaluate when
3560
+ * the form is registered or updated.
3561
+ */
3562
+ getRootForm(formId?: string): FieldTree<unknown> | undefined;
3563
+ /**
3564
+ * Gets the form value, preferring the direct signal if available.
3565
+ *
3566
+ * This method tries in order:
3567
+ * 1. Form value signal (if registered) - reads directly from source signal
3568
+ * 2. Root form's .value() (if registered) - reads from FieldTree
3569
+ * 3. Returns empty object if nothing is registered
3570
+ *
3571
+ * Reading from signals creates reactive dependencies, so computeds will
3572
+ * re-evaluate when form values change.
3573
+ */
3574
+ getFormValue(formId?: string): Record<string, unknown>;
3575
+ /**
3576
+ * Registers form context (field paths and metadata) for a specific form.
3577
+ * This can be used to store additional context information.
3578
+ */
3579
+ registerFormContext(context: Record<string, unknown>, formId?: string): void;
3580
+ /**
3581
+ * Gets form context by id.
3582
+ */
3583
+ getFormContext(formId?: string): Record<string, unknown>;
3584
+ /**
3585
+ * Unregisters a form and its context.
3586
+ * This should be called when a form component is destroyed to prevent memory leaks.
3587
+ */
3588
+ unregisterForm(formId?: string): void;
3589
+ /**
3590
+ * Gets all registered form IDs.
3591
+ */
3592
+ getRegisteredFormIds(): string[];
3593
+ /**
3594
+ * Clears all registered forms. Useful for testing.
3595
+ */
3596
+ clear(): void;
3597
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RootFormRegistryService, never>;
3598
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<RootFormRegistryService>;
3599
+ }
3600
+
3601
+ export { ARRAY_CONTEXT, AddArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, ConsoleLogger, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DynamicForm, DynamicFormError, DynamicFormLogger, DynamicTextPipe, EventBus, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_OPTIONS, FormClearEvent, FormResetEvent, GroupFieldComponent, NextPageEvent, NoopLogger, PageChangeEvent, PreviousPageEvent, RemoveArrayItemEvent, RootFormRegistryService, RowFieldComponent, SubmitEvent, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, baseFieldMapper, buildBaseInputs, dynamicTextToObservable, getArrayLength, groupFieldMapper, interpolateParams, isArrayField, isCheckedField, isContainerField, isDisplayOnlyField, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isValueBearingField, isValueField, omit, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, textFieldMapper, withLoggerConfig };
3394
3602
  export type { ArrayAllowedChildren, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemContext, AsyncCustomValidator, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonLogicContext, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DynamicFormFieldRegistry, DynamicText, EvaluationContext, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCustomValidator, HttpResourceRequest, HttpValidatorConfig, InferFormValue$1 as InferFormValue, LeafFieldTypes, Logger, LogicConfig, MapperFn, NarrowField, NarrowFields, NextButtonOptions, PageAllowedChildren, PageField, Prettify, RegisteredFieldTypes, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };