@ng-forge/dynamic-forms 0.5.0 → 0.5.2

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.
@@ -2,8 +2,8 @@ import * as i0 from '@angular/core';
2
2
  import { inject, DestroyRef, Injector, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { NgComponentOutlet } from '@angular/common';
4
4
  import { outputFromObservable } from '@angular/core/rxjs-interop';
5
- import { i as injectFieldRegistry, E as EventBus, D as DynamicFormLogger, v as validatePageNesting, d as derivedFromDeferred, c as createFieldResolutionPipe, e as emitComponentInitialized } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-WGww2nXx.mjs';
6
5
  import { explicitEffect } from 'ngxtension/explicit-effect';
6
+ import { i as injectFieldRegistry, E as EventBus, D as DynamicFormLogger, c as computeContainerHostClasses, v as validatePageNesting, d as derivedFromDeferred, a as createFieldResolutionPipe, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-9JM7tFkl.mjs';
7
7
 
8
8
  /**
9
9
  * Renders a single page in multi-page (wizard) forms.
@@ -32,21 +32,9 @@ class PageFieldComponent {
32
32
  // ─────────────────────────────────────────────────────────────────────────────
33
33
  // Computed Signals
34
34
  // ─────────────────────────────────────────────────────────────────────────────
35
- hostClasses = computed(() => {
36
- const base = 'df-field df-page-field';
37
- const custom = this.className();
38
- return custom ? `${base} ${custom}` : base;
39
- }, ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
35
+ hostClasses = computed(() => computeContainerHostClasses('page-field', this.className()), ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
40
36
  disabled = computed(() => this.field().disabled || false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
41
- isValid = computed(() => {
42
- const pageField = this.field();
43
- const valid = validatePageNesting(pageField);
44
- if (!valid) {
45
- this.logger.error(`Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +
46
- `Pages cannot contain other pages. Consider using groups or rows for nested structure.`, pageField);
47
- }
48
- return valid;
49
- }, ...(ngDevMode ? [{ debugName: "isValid" }] : []));
37
+ isValid = computed(() => validatePageNesting(this.field()), ...(ngDevMode ? [{ debugName: "isValid" }] : []));
50
38
  rawFieldRegistry = computed(() => this.fieldRegistry.raw, ...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : []));
51
39
  // ─────────────────────────────────────────────────────────────────────────────
52
40
  // Outputs
@@ -84,10 +72,11 @@ class PageFieldComponent {
84
72
  // Private Methods
85
73
  // ─────────────────────────────────────────────────────────────────────────────
86
74
  setupEffects() {
87
- // Emit initialization event when fields are resolved
88
- explicitEffect([this.resolvedFields], ([fields]) => {
89
- if (fields.length > 0) {
90
- emitComponentInitialized(this.eventBus, 'page', this.field().key, this.injector);
75
+ setupContainerInitEffect(this.resolvedFields, this.eventBus, 'page', () => this.field().key, this.injector);
76
+ explicitEffect([this.isValid, this.field], ([valid, pageField]) => {
77
+ if (!valid) {
78
+ this.logger.error(`Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +
79
+ `Pages cannot contain other pages. Consider using groups or rows for nested structure.`, pageField);
91
80
  }
92
81
  });
93
82
  }
@@ -117,4 +106,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
117
106
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: true }] }], isVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVisible", required: true }] }], nextPage: [{ type: i0.Output, args: ["nextPage"] }], previousPage: [{ type: i0.Output, args: ["previousPage"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
118
107
 
119
108
  export { PageFieldComponent as default };
120
- //# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-CrO9WTDw.mjs.map
109
+ //# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-Bm1KS0M6.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-forge-dynamic-forms-page-field.component-Bm1KS0M6.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/page/page-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, Injector, input } from '@angular/core';\nimport { NgComponentOutlet } from '@angular/common';\nimport { outputFromObservable } from '@angular/core/rxjs-interop';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { derivedFromDeferred } from '../../utils/derived-from-deferred/derived-from-deferred';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport { computeContainerHostClasses, setupContainerInitEffect } from '../../utils/container-utils/container-utils';\nimport { PageField, validatePageNesting } from '../../definitions/default/page-field';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { EventBus } from '../../events/event.bus';\nimport { NextPageEvent, PageChangeEvent, PreviousPageEvent } from '../../events/constants';\nimport { FieldDef } from '../../definitions/base/field-def';\nimport { DynamicFormLogger } from '../../providers/features/logger/logger.token';\n\n/**\n * Renders a single page in multi-page (wizard) forms.\n *\n * Visibility is controlled by the PageOrchestrator via the isVisible input.\n * Pages cannot be nested within other pages - validation prevents this.\n * Field values are flattened into the parent form (no nesting under page key).\n */\n@Component({\n selector: 'section[page-field]',\n imports: [NgComponentOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n <ng-container *ngComponentOutlet=\"field.component; injector: field.injector; inputs: field.inputs()\" />\n }\n `,\n styleUrl: './page-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n '[class.disabled]': 'disabled()',\n '[class.df-page-visible]': 'isVisible()',\n '[class.df-page-hidden]': '!isVisible()',\n '[attr.aria-hidden]': '!isVisible()',\n '[attr.data-page-index]': 'pageIndex()',\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class PageFieldComponent {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly injector = inject(Injector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<PageField>();\n key = input.required<string>();\n className = input<string>();\n pageIndex = input.required<number>();\n isVisible = input.required<boolean>();\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly hostClasses = computed(() => computeContainerHostClasses('page-field', this.className()));\n\n readonly disabled = computed(() => this.field().disabled || false);\n\n readonly isValid = computed(() => validatePageNesting(this.field()));\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly nextPage = outputFromObservable(this.eventBus.on<NextPageEvent>('next-page'));\n readonly previousPage = outputFromObservable(this.eventBus.on<PreviousPageEvent>('previous-page'));\n readonly pageChange = outputFromObservable(this.eventBus.on<PageChangeEvent>('page-change'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => {\n if (!this.isValid()) {\n return [];\n }\n return this.field().fields || [];\n });\n\n protected readonly resolvedFields = derivedFromDeferred(\n this.fieldsSource,\n createFieldResolutionPipe(() => ({\n loadTypeComponent: (type: string) => this.fieldRegistry.loadTypeComponent(type),\n registry: this.rawFieldRegistry(),\n injector: this.injector,\n destroyRef: this.destroyRef,\n onError: (fieldDef: FieldDef<unknown>, error: unknown) => {\n const fieldKey = fieldDef.key || '<no key>';\n this.logger.error(\n `Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within page '${this.field().key}'. Ensure the field type is registered in your field registry.`,\n error,\n );\n },\n })),\n { initialValue: [] as ResolvedField[], injector: this.injector },\n );\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Constructor\n // ─────────────────────────────────────────────────────────────────────────────\n\n constructor() {\n this.setupEffects();\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Private Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n private setupEffects(): void {\n setupContainerInitEffect(this.resolvedFields, this.eventBus, 'page', () => this.field().key, this.injector);\n\n explicitEffect([this.isValid, this.field], ([valid, pageField]) => {\n if (!valid) {\n this.logger.error(\n `Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +\n `Pages cannot contain other pages. Consider using groups or rows for nested structure.`,\n pageField,\n );\n }\n });\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { AsyncPipe } from '@angular/common';
4
- import { a as DynamicTextPipe } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-WGww2nXx.mjs';
4
+ import { b as DynamicTextPipe } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-9JM7tFkl.mjs';
5
5
 
6
6
  class TextFieldComponent {
7
7
  key = input.required(...(ngDevMode ? [{ debugName: "key" }] : []));
@@ -110,4 +110,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
110
110
  }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
111
111
 
112
112
  export { TextFieldComponent as default };
113
- //# sourceMappingURL=ng-forge-dynamic-forms-text-field.component-BcFx_kOz.mjs.map
113
+ //# sourceMappingURL=ng-forge-dynamic-forms-text-field.component-DmJwXtzI.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-forge-dynamic-forms-text-field.component-BcFx_kOz.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/text/text-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { AsyncPipe } from '@angular/common';\nimport { DynamicTextPipe } from '../../pipes/dynamic-text/dynamic-text.pipe';\nimport { TextProps } from '../../definitions/default/text-field';\nimport { DynamicText } from '../../models/types/dynamic-text';\n\n@Component({\n selector: 'df-text',\n template: `\n @switch (elementType()) {\n @case ('p') {\n <p class=\"df-text df-text-p\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </p>\n }\n @case ('h1') {\n <h1 class=\"df-text df-text-h1\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h1>\n }\n @case ('h2') {\n <h2 class=\"df-text df-text-h2\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h2>\n }\n @case ('h3') {\n <h3 class=\"df-text df-text-h3\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h3>\n }\n @case ('h4') {\n <h4 class=\"df-text df-text-h4\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h4>\n }\n @case ('h5') {\n <h5 class=\"df-text df-text-h5\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h5>\n }\n @case ('h6') {\n <h6 class=\"df-text df-text-h6\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h6>\n }\n @case ('span') {\n <span class=\"df-text df-text-span\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </span>\n }\n }\n `,\n styleUrl: './text-field.component.scss',\n imports: [AsyncPipe, DynamicTextPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n '[attr.hidden]': 'hidden() || null',\n },\n})\nexport default class TextFieldComponent {\n readonly key = input.required<string>();\n readonly label = input.required<DynamicText>();\n readonly className = input<string>();\n readonly tabIndex = input<number>();\n readonly hidden = input<boolean>();\n readonly props = input<TextProps>();\n\n readonly elementType = computed(() => this.props()?.elementType || 'p');\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-forge-dynamic-forms-text-field.component-DmJwXtzI.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/text/text-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { AsyncPipe } from '@angular/common';\nimport { DynamicTextPipe } from '../../pipes/dynamic-text/dynamic-text.pipe';\nimport { TextProps } from '../../definitions/default/text-field';\nimport { DynamicText } from '../../models/types/dynamic-text';\n\n@Component({\n selector: 'df-text',\n template: `\n @switch (elementType()) {\n @case ('p') {\n <p class=\"df-text df-text-p\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </p>\n }\n @case ('h1') {\n <h1 class=\"df-text df-text-h1\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h1>\n }\n @case ('h2') {\n <h2 class=\"df-text df-text-h2\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h2>\n }\n @case ('h3') {\n <h3 class=\"df-text df-text-h3\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h3>\n }\n @case ('h4') {\n <h4 class=\"df-text df-text-h4\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h4>\n }\n @case ('h5') {\n <h5 class=\"df-text df-text-h5\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h5>\n }\n @case ('h6') {\n <h6 class=\"df-text df-text-h6\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h6>\n }\n @case ('span') {\n <span class=\"df-text df-text-span\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </span>\n }\n }\n `,\n styleUrl: './text-field.component.scss',\n imports: [AsyncPipe, DynamicTextPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n '[attr.hidden]': 'hidden() || null',\n },\n})\nexport default class TextFieldComponent {\n readonly key = input.required<string>();\n readonly label = input.required<DynamicText>();\n readonly className = input<string>();\n readonly tabIndex = input<number>();\n readonly hidden = input<boolean>();\n readonly props = input<TextProps>();\n\n readonly elementType = computed(() => this.props()?.elementType || 'p');\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export { Q as ARRAY_CONTEXT, A as AddArrayItemEvent, Y as ArrayFieldComponent, a3 as BUILT_IN_FIELDS, C as ConsoleLogger, T as DEFAULT_PROPS, U as DEFAULT_VALIDATION_MESSAGES, b as DynamicForm, B as DynamicFormError, D as DynamicFormLogger, a as DynamicTextPipe, E as EventBus, O as FIELD_REGISTRY, V as FIELD_SIGNAL_CONTEXT, W as FORM_OPTIONS, F as FormClearEvent, u as FormResetEvent, Z as GroupFieldComponent, x as NextPageEvent, N as NoopLogger, P as PageChangeEvent, y as PreviousPageEvent, R as RemoveArrayItemEvent, a8 as RootFormRegistryService, _ as RowFieldComponent, S as SubmitEvent, a7 as applyMetaToElement, $ as applyValidator, a0 as applyValidators, I as arrayFieldMapper, G as baseFieldMapper, H as buildBaseInputs, X as dynamicTextToObservable, z as formConfig, a1 as getArrayLength, J as groupFieldMapper, a6 as interpolateParams, l as isArrayField, h as isCheckedField, m as isContainerField, n as isDisplayOnlyField, a4 as isEqual, f as isFormStateCondition, o as isGroupField, q as isLeafField, s as isPageField, k as isRowField, t as isValueBearingField, j as isValueField, a5 as omit, K as pageFieldMapper, p as provideDynamicForm, g as resolveNextButtonDisabled, r as resolveSubmitButtonDisabled, a2 as resolveTokens, L as rowFieldMapper, M as textFieldMapper, w as withLoggerConfig } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-WGww2nXx.mjs';
1
+ export { a3 as ARRAY_CONTEXT, F as AppendArrayItemEvent, a9 as ArrayFieldComponent, ag as BUILT_IN_FIELDS, C as ConsoleLogger, a4 as DEFAULT_PROPS, a5 as DEFAULT_VALIDATION_MESSAGES, e as DynamicForm, W as DynamicFormError, D as DynamicFormLogger, b as DynamicTextPipe, E as EventBus, a2 as FIELD_REGISTRY, a6 as FIELD_SIGNAL_CONTEXT, a7 as FORM_OPTIONS, G as FormClearEvent, H as FormResetEvent, aa as GroupFieldComponent, I as InsertArrayItemEvent, J as NextPageEvent, N as NoopLogger, P as PageChangeEvent, K as PopArrayItemEvent, L as PrependArrayItemEvent, M as PreviousPageEvent, R as RemoveAtIndexEvent, al as RootFormRegistryService, ab as RowFieldComponent, S as ShiftArrayItemEvent, O as SubmitEvent, ak as applyMetaToElement, ac as applyValidator, ad as applyValidators, Q as arrayEvent, Z as arrayFieldMapper, X as baseFieldMapper, Y as buildBaseInputs, T as createField, a8 as dynamicTextToObservable, U as field, V as formConfig, ae as getArrayLength, _ as groupFieldMapper, h as hasFormValue, aj as interpolateParams, t as isArrayField, m as isCheckedField, u as isContainerField, x as isDisplayOnlyField, ah as isEqual, g as isFormStateCondition, y as isGroupField, z as isLeafField, A as isPageField, q as isPropertyDerivationLogicConfig, o as isRowField, B as isValueBearingField, n as isValueField, ai as omit, $ as pageFieldMapper, p as provideDynamicForm, j as resolveNextButtonDisabled, l as resolveNonFieldDisabled, k as resolveNonFieldHidden, r as resolveSubmitButtonDisabled, af as resolveTokens, a0 as rowFieldMapper, a1 as textFieldMapper, f as withEventFormValue, w as withLoggerConfig } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-9JM7tFkl.mjs';
2
2
  //# sourceMappingURL=ng-forge-dynamic-forms.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-forge/dynamic-forms",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Type-safe, signal-powered dynamic forms for Angular. Build complex forms from configuration with full TypeScript inference.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,15 +1,21 @@
1
- import { FormEvent, FieldDef, FormEventConstructor, BaseCheckedField, BaseValueField, DynamicText, FieldMeta, FieldOption, ValidationMessages } from '@ng-forge/dynamic-forms';
2
- export { isEqual } from '@ng-forge/dynamic-forms';
1
+ import { FormEvent, FieldDef, FormEventConstructor, StateLogicConfig, BaseCheckedField, BaseValueField, DynamicText, FieldMeta, FieldOption, ValidationMessages, ArrayAllowedChildren, NonFieldLogicConfig, NextPageEvent, PreviousPageEvent, FieldWithValidation } from '@ng-forge/dynamic-forms';
2
+ export { NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, isEqual, resolveNonFieldDisabled, resolveNonFieldHidden } from '@ng-forge/dynamic-forms';
3
3
  import { Signal, Injector, ElementRef } from '@angular/core';
4
4
  import { FieldTree } from '@angular/forms/signals';
5
5
 
6
6
  /**
7
- * Event arguments that can contain static values or tokens to be resolved at runtime
8
- * Tokens supported: $key, $index, $arrayKey, formValue
7
+ * Event arguments that can contain static values or tokens to be resolved at runtime.
8
+ * Tokens supported: $key, $index, $arrayKey, $template, formValue
9
+ *
10
+ * Used by the generic button type when manually configuring events.
9
11
  */
10
12
  type EventArgs = readonly (string | number | boolean | null | undefined)[];
11
13
  /**
12
- * Interface for button fields
14
+ * Base interface for button fields.
15
+ *
16
+ * This is used by the generic 'button' type which requires explicit event configuration.
17
+ * For specific button types (submit, next, previous, array buttons), use the
18
+ * pre-configured types from the UI library instead.
13
19
  */
14
20
  interface ButtonField<TProps, TEvent extends FormEvent> extends FieldDef<TProps> {
15
21
  type: 'button';
@@ -20,6 +26,7 @@ interface ButtonField<TProps, TEvent extends FormEvent> extends FieldDef<TProps>
20
26
  * - $key: The current field key
21
27
  * - $index: The array index (if inside an array field)
22
28
  * - $arrayKey: The parent array field key (if inside an array field)
29
+ * - $template: The template for array item creation (for array add buttons)
23
30
  * - formValue: Access to the current form value for indexing
24
31
  *
25
32
  * @example
@@ -27,6 +34,8 @@ interface ButtonField<TProps, TEvent extends FormEvent> extends FieldDef<TProps>
27
34
  * eventArgs: ['contacts', 0]
28
35
  */
29
36
  eventArgs?: EventArgs;
37
+ /** Logic rules for dynamic disabled state (overrides form-level defaults) */
38
+ logic?: StateLogicConfig[];
30
39
  }
31
40
 
32
41
  interface CheckboxField<TProps> extends BaseCheckedField<TProps> {
@@ -384,26 +393,43 @@ interface ToggleField<TProps> extends BaseCheckedField<TProps> {
384
393
  }
385
394
 
386
395
  /**
387
- * Context for value field mapping, containing resolved field tree.
388
- * Used by specialized mappers to avoid duplicate context resolution.
396
+ * Context for value field mapping, containing the injected context reference.
397
+ * Used by specialized mappers to avoid duplicate injection.
398
+ *
399
+ * Note: The field tree is resolved lazily via getFieldTree() to support
400
+ * reactive form access (e.g., for array items where the form structure
401
+ * may not exist at injection time but becomes available later).
389
402
  */
390
403
  interface ValueFieldContext {
391
- fieldTree: FieldTree<unknown> | undefined;
404
+ /**
405
+ * Gets the field tree for a given key from the form context.
406
+ * This is reactive for array items - the form getter is evaluated each time,
407
+ * allowing the computed to re-run when the root form structure updates.
408
+ */
409
+ getFieldTree: (fieldKey: string) => FieldTree<unknown> | undefined;
392
410
  }
393
411
  /**
394
- * Resolves the field tree from the form context.
412
+ * Creates a value field context from the injected FIELD_SIGNAL_CONTEXT.
395
413
  * Must be called within an injection context.
396
414
  *
397
- * Uses type-safe field tree utilities to access child FieldTrees from the parent form.
415
+ * With direct root form binding for array items, the FIELD_SIGNAL_CONTEXT.form
416
+ * uses a getter that evaluates reactively. This means:
417
+ * - For regular fields: direct access to the form's FieldTree
418
+ * - For array items: the getter resolves rootForm['arrayKey'][index] dynamically
419
+ *
420
+ * The returned context provides a getFieldTree function that should be called
421
+ * inside a computed to establish proper reactive dependencies.
398
422
  *
399
- * @param fieldKey The key of the field to resolve
400
- * @returns The resolved context with field tree
423
+ * @returns The value field context with reactive field tree accessor
401
424
  */
402
- declare function resolveValueFieldContext(fieldKey: string): ValueFieldContext;
425
+ declare function resolveValueFieldContext(): ValueFieldContext;
403
426
  /**
404
427
  * Builds the base inputs for a value field.
405
428
  * This is a helper function used by valueFieldMapper and specialized mappers.
406
429
  *
430
+ * Note: This function should be called inside a computed signal to ensure
431
+ * proper reactive dependency tracking for the field tree resolution.
432
+ *
407
433
  * @param fieldDef The value field definition
408
434
  * @param ctx The resolved value field context
409
435
  * @param defaultProps Default props from the form configuration (may be undefined if not configured)
@@ -417,6 +443,12 @@ declare function buildValueFieldInputs<TProps, TValue = unknown>(fieldDef: BaseV
417
443
  * Value fields are input fields that contribute to the form's value (text, number, etc.).
418
444
  * This mapper injects FIELD_SIGNAL_CONTEXT to access the form structure and retrieve the field proxy.
419
445
  *
446
+ * For array items, the FIELD_SIGNAL_CONTEXT.form uses a reactive getter that resolves
447
+ * rootForm['arrayKey'][index] dynamically. This means:
448
+ * - Zod/StandardSchema validation errors are automatically available
449
+ * - The field tree updates reactively when the root form structure changes
450
+ * - Newly added array items will get their field tree once the form value updates
451
+ *
420
452
  * For fields with specific properties (select, datepicker, textarea, slider), use the specialized mappers:
421
453
  * - selectFieldMapper: for fields with options (select, radio, multi-checkbox)
422
454
  * - datepickerFieldMapper: for fields with minDate, maxDate, startAt
@@ -447,6 +479,9 @@ declare function checkboxFieldMapper(fieldDef: BaseCheckedField<unknown>): Signa
447
479
  * - maxDate: Maximum selectable date
448
480
  * - startAt: Initial date to display when opening the picker
449
481
  *
482
+ * Date values are automatically converted from strings to Date objects
483
+ * since UI libraries (Material, PrimeNG, etc.) expect Date objects.
484
+ *
450
485
  * @param fieldDef The datepicker field definition
451
486
  * @returns Signal containing Record of input names to values for ngComponentOutlet
452
487
  */
@@ -466,6 +501,191 @@ type FieldWithOptions<T = unknown, TProps = unknown> = SelectField<T, TProps> |
466
501
  */
467
502
  declare function optionsFieldMapper<T, TProps>(fieldDef: FieldWithOptions<T, TProps>): Signal<Record<string, unknown>>;
468
503
 
504
+ /**
505
+ * Maps a button field to component inputs.
506
+ *
507
+ * Unlike value field mappers, button fields do not participate in form values
508
+ * and do not need field tree resolution or validation messages.
509
+ *
510
+ * Button-specific properties:
511
+ * - event: The FormEvent constructor to emit when clicked
512
+ * - eventArgs: Optional arguments to pass to the event constructor
513
+ *
514
+ * Hidden and disabled states are resolved using non-field logic resolvers which consider:
515
+ * 1. Explicit `hidden: true` / `disabled: true` on the field definition
516
+ * 2. Field-level `logic` array with `type: 'hidden'` or `type: 'disabled'` conditions
517
+ *
518
+ * @param fieldDef The button field definition
519
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
520
+ */
521
+ declare function buttonFieldMapper<TProps, TEvent extends FormEvent>(fieldDef: ButtonField<TProps, TEvent>): Signal<Record<string, unknown>>;
522
+
523
+ /**
524
+ * Context for array button mapping, containing resolved array information.
525
+ * Used by array button mappers to build event context.
526
+ */
527
+ interface ArrayButtonContext {
528
+ /** The target array key (from explicit arrayKey or ARRAY_CONTEXT) */
529
+ targetArrayKey: string | undefined;
530
+ /** Computed signal that returns the current array index (-1 if not inside array) */
531
+ indexSignal: Signal<number>;
532
+ /** The current form value for token resolution */
533
+ formValue: Record<string, unknown>;
534
+ /** Whether this button is inside an array context */
535
+ isInsideArray: boolean;
536
+ }
537
+ /**
538
+ * Event context passed to button components for array operations.
539
+ */
540
+ interface ArrayButtonEventContext {
541
+ /** The button field key */
542
+ key: string;
543
+ /** The current array index (-1 if outside array) */
544
+ index: number;
545
+ /** The target array key */
546
+ arrayKey: string;
547
+ /** The form value for token resolution */
548
+ formValue: Record<string, unknown>;
549
+ /**
550
+ * Optional template for add operations.
551
+ * Can be a single field (primitive item) or array of fields (object item).
552
+ */
553
+ template?: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
554
+ }
555
+ /**
556
+ * Resolves array button context for button mappers.
557
+ *
558
+ * Handles both inside-array (context from ARRAY_CONTEXT) and
559
+ * outside-array (explicit arrayKey) placements.
560
+ *
561
+ * Must be called in injection context.
562
+ *
563
+ * @param fieldKey The button field key (for logging)
564
+ * @param buttonType The button type name (for logging)
565
+ * @param explicitArrayKey Optional explicit arrayKey from field definition
566
+ * @returns The array button context with reactive index signal
567
+ */
568
+ declare function resolveArrayButtonContext(fieldKey: string, buttonType: string, explicitArrayKey?: string): ArrayButtonContext;
569
+ /**
570
+ * Builds the event context passed to the button component.
571
+ *
572
+ * Should be called inside a computed to track index signal changes.
573
+ *
574
+ * @param fieldKey The button field key
575
+ * @param ctx The resolved array button context
576
+ * @param template Optional template for add operations (single field or array of fields)
577
+ * @returns The event context for the button component
578
+ */
579
+ declare function buildArrayButtonEventContext(fieldKey: string, ctx: ArrayButtonContext, template?: ArrayAllowedChildren | readonly ArrayAllowedChildren[]): ArrayButtonEventContext;
580
+ /**
581
+ * Event argument type - supports static values and tokens.
582
+ * Tokens: $key, $index, $arrayKey, $template, formValue
583
+ */
584
+ type EventArg = string | number | boolean | null | undefined;
585
+ /**
586
+ * Resolves eventArgs with defaults for array buttons.
587
+ *
588
+ * @param fieldEventArgs The eventArgs from field definition (may be undefined)
589
+ * @param defaultEventArgs The default eventArgs to use if not specified
590
+ * @returns The resolved eventArgs array
591
+ */
592
+ declare function resolveArrayButtonEventArgs(fieldEventArgs: readonly EventArg[] | undefined, defaultEventArgs?: readonly EventArg[]): readonly EventArg[];
593
+
594
+ /**
595
+ * Base interface for array add button fields (append, prepend, insert).
596
+ * Template is required to define the structure for new items.
597
+ */
598
+ interface BaseArrayAddButtonField<TProps = unknown> {
599
+ key: string;
600
+ type: string;
601
+ label?: string;
602
+ disabled?: boolean;
603
+ hidden?: boolean;
604
+ className?: string;
605
+ props?: TProps;
606
+ /** Target array key (required when outside array, optional inside) */
607
+ arrayKey?: string;
608
+ /**
609
+ * Template defining the field structure for new items. REQUIRED.
610
+ * - Single field (ArrayAllowedChildren): Creates a primitive item (field's value is extracted directly)
611
+ * - Array of fields (ArrayAllowedChildren[]): Creates an object item (fields merged into object)
612
+ */
613
+ template: ArrayAllowedChildren | readonly ArrayAllowedChildren[];
614
+ /** Optional custom eventArgs */
615
+ eventArgs?: readonly EventArg[];
616
+ /** Logic rules for dynamic hidden/disabled state (only hidden and disabled are supported) */
617
+ logic?: NonFieldLogicConfig[];
618
+ }
619
+ /**
620
+ * Base interface for array remove button fields (remove, pop, shift).
621
+ * No template needed - removes existing items.
622
+ */
623
+ interface BaseArrayRemoveButtonField<TProps = unknown> {
624
+ key: string;
625
+ type: string;
626
+ label?: string;
627
+ disabled?: boolean;
628
+ hidden?: boolean;
629
+ className?: string;
630
+ props?: TProps;
631
+ /** Target array key (required when outside array, optional inside) */
632
+ arrayKey?: string;
633
+ /** Optional custom eventArgs */
634
+ eventArgs?: readonly EventArg[];
635
+ /** Logic rules for dynamic hidden/disabled state (only hidden and disabled are supported) */
636
+ logic?: NonFieldLogicConfig[];
637
+ }
638
+ /**
639
+ * Interface for insert array item button fields.
640
+ * Extends add button with required index.
641
+ */
642
+ interface BaseInsertArrayItemButtonField<TProps = unknown> extends BaseArrayAddButtonField<TProps> {
643
+ /** The index at which to insert the new item */
644
+ index: number;
645
+ }
646
+ /**
647
+ * Mapper for add array item button — appends new item to end of array.
648
+ * Template is REQUIRED to define the field structure for new items.
649
+ */
650
+ declare function addArrayItemButtonMapper<TProps>(fieldDef: BaseArrayAddButtonField<TProps>): Signal<Record<string, unknown>>;
651
+ /**
652
+ * Mapper for prepend array item button — inserts new item at beginning of array.
653
+ * Template is REQUIRED to define the field structure for new items.
654
+ */
655
+ declare function prependArrayItemButtonMapper<TProps>(fieldDef: BaseArrayAddButtonField<TProps>): Signal<Record<string, unknown>>;
656
+ /**
657
+ * Mapper for insert array item button — inserts new item at specific index.
658
+ * Template and index are REQUIRED.
659
+ */
660
+ declare function insertArrayItemButtonMapper<TProps>(fieldDef: BaseInsertArrayItemButtonField<TProps>): Signal<Record<string, unknown>>;
661
+ /**
662
+ * Mapper for remove array item button.
663
+ * Inside array: RemoveAtIndexEvent. Outside array: PopArrayItemEvent.
664
+ */
665
+ declare function removeArrayItemButtonMapper<TProps>(fieldDef: BaseArrayRemoveButtonField<TProps>): Signal<Record<string, unknown>>;
666
+ /** Mapper for pop array item button — removes the last item from array. */
667
+ declare function popArrayItemButtonMapper<TProps>(fieldDef: BaseArrayRemoveButtonField<TProps>): Signal<Record<string, unknown>>;
668
+ /** Mapper for shift array item button — removes the first item from array. */
669
+ declare function shiftArrayItemButtonMapper<TProps>(fieldDef: BaseArrayRemoveButtonField<TProps>): Signal<Record<string, unknown>>;
670
+
671
+ /**
672
+ * Base interface for navigation button fields (submit, next, previous).
673
+ */
674
+ type BaseNavigationButtonField<TProps = unknown> = ButtonField<TProps, SubmitEvent | NextPageEvent | PreviousPageEvent> & {
675
+ type: string;
676
+ /** Logic rules for dynamic disabled state */
677
+ logic?: FieldWithValidation['logic'];
678
+ };
679
+ /**
680
+ * Mapper for submit button — configures native form submission via type="submit".
681
+ * Triggers native form submission rather than dispatching events directly.
682
+ */
683
+ declare function submitButtonFieldMapper<TProps>(fieldDef: BaseNavigationButtonField<TProps>): Signal<Record<string, unknown>>;
684
+ /** Mapper for next page button — preconfigures NextPageEvent. */
685
+ declare function nextButtonFieldMapper<TProps>(fieldDef: BaseNavigationButtonField<TProps>): Signal<Record<string, unknown>>;
686
+ /** Mapper for previous page button — preconfigures PreviousPageEvent. */
687
+ declare function previousButtonFieldMapper<TProps>(fieldDef: BaseNavigationButtonField<TProps>): Signal<Record<string, unknown>>;
688
+
469
689
  /**
470
690
  * Resolved validation error with interpolated message
471
691
  */
@@ -495,8 +715,8 @@ interface ResolvedError {
495
715
  declare function createResolvedErrorsSignal<T>(field: Signal<FieldTree<T>>, validationMessages: Signal<ValidationMessages | undefined>, defaultValidationMessages?: Signal<ValidationMessages | undefined>, injector?: Injector): Signal<ResolvedError[]>;
496
716
 
497
717
  /**
498
- * Computed signal that determines if errors should be displayed
499
- * Based on field's invalid, touched, and error count
718
+ * Computed signal that determines if errors should be displayed.
719
+ * Based on field's invalid, touched, and error count.
500
720
  *
501
721
  * @param field - Signal containing FieldTree
502
722
  * @returns Signal<boolean> - True if errors should be displayed
@@ -568,5 +788,5 @@ interface MetaTrackingOptions {
568
788
  */
569
789
  declare function setupMetaTracking(elementRef: ElementRef<HTMLElement>, meta: Signal<FieldMeta | undefined>, options?: MetaTrackingOptions): void;
570
790
 
571
- export { buildValueFieldInputs, checkboxFieldMapper, createResolvedErrorsSignal, datepickerFieldMapper, optionsFieldMapper, resolveValueFieldContext, setupMetaTracking, shouldShowErrors, valueFieldMapper };
572
- export type { Autocapitalize, AutocompleteValue, ButtonField, CheckboxField, DatepickerField, DatepickerProps, EnterKeyHint, EventArgs, FieldWithOptions, HtmlInputType, InferInputValue, InputField, InputMeta, InputMode, InputProps, InputType, InputTypeToValueType, MetaTrackingOptions, MultiCheckboxField, NumericInputType, RadioField, ResolvedError, SelectField, SelectProps, SliderField, StringInputType, TextareaField, TextareaMeta, TextareaProps, TextareaWrap, ToggleField, ValueFieldContext };
791
+ export { addArrayItemButtonMapper, buildArrayButtonEventContext, buildValueFieldInputs, buttonFieldMapper, checkboxFieldMapper, createResolvedErrorsSignal, datepickerFieldMapper, insertArrayItemButtonMapper, nextButtonFieldMapper, optionsFieldMapper, popArrayItemButtonMapper, prependArrayItemButtonMapper, previousButtonFieldMapper, removeArrayItemButtonMapper, resolveArrayButtonContext, resolveArrayButtonEventArgs, resolveValueFieldContext, setupMetaTracking, shiftArrayItemButtonMapper, shouldShowErrors, submitButtonFieldMapper, valueFieldMapper };
792
+ export type { ArrayButtonContext, ArrayButtonEventContext, Autocapitalize, AutocompleteValue, BaseArrayAddButtonField, BaseArrayRemoveButtonField, BaseInsertArrayItemButtonField, BaseNavigationButtonField, ButtonField, CheckboxField, DatepickerField, DatepickerProps, EnterKeyHint, EventArg, EventArgs, FieldWithOptions, HtmlInputType, InferInputValue, InputField, InputMeta, InputMode, InputProps, InputType, InputTypeToValueType, MetaTrackingOptions, MultiCheckboxField, NumericInputType, RadioField, ResolvedError, SelectField, SelectProps, SliderField, StringInputType, TextareaField, TextareaMeta, TextareaProps, TextareaWrap, ToggleField, ValueFieldContext };