@ng-forge/dynamic-forms 1.2.0-next.3 → 1.2.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (16) hide show
  1. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-B9OfjKPI.mjs → ng-forge-dynamic-forms-array-field.component-BLPWSurR.mjs} +2 -2
  2. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-B9OfjKPI.mjs.map → ng-forge-dynamic-forms-array-field.component-BLPWSurR.mjs.map} +1 -1
  3. package/fesm2022/{ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-VJBOH393.mjs → ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-jHiY95pr.mjs} +2 -2
  4. package/fesm2022/{ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-VJBOH393.mjs.map → ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-jHiY95pr.mjs.map} +1 -1
  5. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-o49q6akV.mjs → ng-forge-dynamic-forms-container-field.component-ug5tp6kt.mjs} +2 -2
  6. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-o49q6akV.mjs.map → ng-forge-dynamic-forms-container-field.component-ug5tp6kt.mjs.map} +1 -1
  7. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-DA-FW30T.mjs → ng-forge-dynamic-forms-group-field.component-DyD00a1U.mjs} +2 -2
  8. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-DA-FW30T.mjs.map → ng-forge-dynamic-forms-group-field.component-DyD00a1U.mjs.map} +1 -1
  9. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CUihkwGC.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BGRxIQ2V.mjs} +160 -140
  10. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BGRxIQ2V.mjs.map +1 -0
  11. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-P9O4eNvS.mjs → ng-forge-dynamic-forms-page-field.component-B7A2v1vN.mjs} +2 -2
  12. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-P9O4eNvS.mjs.map → ng-forge-dynamic-forms-page-field.component-B7A2v1vN.mjs.map} +1 -1
  13. package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
  14. package/package.json +1 -1
  15. package/types/ng-forge-dynamic-forms.d.ts +36 -14
  16. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CUihkwGC.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, DestroyRef, Injector, EnvironmentInjector, viewChild, TemplateRef, ViewContainerRef, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { i as injectFieldRegistry, c as createFieldResolutionPipe, r as resolveWrappers, a as isSameWrapperChain, b as createWrapperChainController, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-CUihkwGC.mjs';
3
+ import { i as injectFieldRegistry, c as createFieldResolutionPipe, r as resolveWrappers, a as isSameWrapperChain, b as createWrapperChainController, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-BGRxIQ2V.mjs';
4
4
  import { EventBus, DynamicFormLogger, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
5
5
  import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
6
6
 
@@ -86,4 +86,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
86
86
  }], ctorParameters: () => [], propDecorators: { childrenTpl: [{ type: i0.ViewChild, args: ['childrenTpl', { ...{ read: TemplateRef }, isSignal: true }] }], wrapperContainer: [{ type: i0.ViewChild, args: ['wrapperContainer', { ...{ read: ViewContainerRef }, isSignal: true }] }], 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 }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }] } });
87
87
 
88
88
  export { ContainerFieldComponent, ContainerFieldComponent as default };
89
- //# sourceMappingURL=ng-forge-dynamic-forms-container-field.component-o49q6akV.mjs.map
89
+ //# sourceMappingURL=ng-forge-dynamic-forms-container-field.component-ug5tp6kt.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-forge-dynamic-forms-container-field.component-o49q6akV.mjs","mappings":";;;;;;AAoDc;AACK;;AAEA;AACE;AACF;AACA;AACA;;AAGA;AACA;;;;;AAIjB;;;;AAGS;;AACA;;;;AAGQ;;AAEE;AAGf;AACA;;;AAGA;AACE;;;AAKI;;AAIP;AAIc;AAIjB;;AAGE;;;AAGE;AACD;;;AAxDgB;;;;;;;;;AAZlB;;;;AAXS;;;;;;;;;;AAcR;AACA;AACA;AACA;AACA;AACA;;AAcgD","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/container/container-field.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n EnvironmentInjector,\n inject,\n Injector,\n input,\n TemplateRef,\n viewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport { computeContainerHostClasses, setupContainerInitEffect } from '../../utils/container-utils/container-utils';\nimport { ContainerField } from '@ng-forge/dynamic-forms/internal';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { EventBus } from '@ng-forge/dynamic-forms/internal';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\nimport { WRAPPER_AUTO_ASSOCIATIONS } from '@ng-forge/dynamic-forms/internal';\nimport { DEFAULT_WRAPPERS } from '@ng-forge/dynamic-forms/internal';\nimport { isSameWrapperChain, resolveWrappers } from '../../utils/resolve-wrappers/resolve-wrappers';\nimport { createWrapperChainController } from '../../utils/wrapper-chain/wrapper-chain-controller';\n\n/** Layout container that wraps child fields with UI chrome. */\n@Component({\n selector: 'div[container-field]',\n imports: [DfFieldOutlet],\n template: `\n <ng-template #childrenTpl>\n @for (field of resolvedFields(); track field.key) {\n @if (!field.hidden()) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n }\n </ng-template>\n <ng-container #wrapperContainer></ng-container>\n `,\n styleUrl: './container-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n '[class.disabled]': 'disabled()',\n '[class.df-container-hidden]': 'hidden()',\n '[attr.aria-hidden]': 'hidden() || null',\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class ContainerFieldComponent {\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly injector = inject(Injector);\n protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n private readonly wrapperAutoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);\n private readonly defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });\n\n private readonly childrenTpl = viewChild.required('childrenTpl', { read: TemplateRef });\n private readonly wrapperContainer = viewChild.required('wrapperContainer', { read: ViewContainerRef });\n\n field = input.required<ContainerField>();\n key = input.required<string>();\n className = input<string>();\n hidden = input(false);\n\n readonly hostClasses = computed(() => computeContainerHostClasses('container', this.className()));\n readonly disabled = computed(() => this.field().disabled || false);\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n private readonly fieldsSource = computed(() => (this.field().fields || []) as FieldDef<unknown>[]);\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\n const containerKey = this.field().key || '<no key>';\n this.logger.error(\n `Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within container '${containerKey}'. 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 private readonly wrappers = computed(() => resolveWrappers(this.field(), this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), {\n equal: isSameWrapperChain,\n });\n\n constructor() {\n setupContainerInitEffect(this.resolvedFields, this.eventBus, 'container', () => this.field().key, this.injector);\n\n createWrapperChainController({\n vcr: this.wrapperContainer,\n wrappers: this.wrappers,\n renderInnermost: (slot) => slot.createEmbeddedView(this.childrenTpl()),\n });\n }\n}\n\nexport { ContainerFieldComponent };\n"]}
1
+ {"version":3,"file":"ng-forge-dynamic-forms-container-field.component-ug5tp6kt.mjs","mappings":";;;;;;AAoDc;AACK;;AAEA;AACE;AACF;AACA;AACA;;AAGA;AACA;;;;;AAIjB;;;;AAGS;;AACA;;;;AAGQ;;AAEE;AAGf;AACA;;;AAGA;AACE;;;AAKI;;AAIP;AAIc;AAIjB;;AAGE;;;AAGE;AACD;;;AAxDgB;;;;;;;;;AAZlB;;;;AAXS;;;;;;;;;;AAcR;AACA;AACA;AACA;AACA;AACA;;AAcgD","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/container/container-field.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n EnvironmentInjector,\n inject,\n Injector,\n input,\n TemplateRef,\n viewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport { computeContainerHostClasses, setupContainerInitEffect } from '../../utils/container-utils/container-utils';\nimport { ContainerField } from '@ng-forge/dynamic-forms/internal';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { EventBus } from '@ng-forge/dynamic-forms/internal';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\nimport { WRAPPER_AUTO_ASSOCIATIONS } from '@ng-forge/dynamic-forms/internal';\nimport { DEFAULT_WRAPPERS } from '@ng-forge/dynamic-forms/internal';\nimport { isSameWrapperChain, resolveWrappers } from '../../utils/resolve-wrappers/resolve-wrappers';\nimport { createWrapperChainController } from '../../utils/wrapper-chain/wrapper-chain-controller';\n\n/** Layout container that wraps child fields with UI chrome. */\n@Component({\n selector: 'div[container-field]',\n imports: [DfFieldOutlet],\n template: `\n <ng-template #childrenTpl>\n @for (field of resolvedFields(); track field.key) {\n @if (!field.hidden()) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n }\n </ng-template>\n <ng-container #wrapperContainer></ng-container>\n `,\n styleUrl: './container-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n '[class.disabled]': 'disabled()',\n '[class.df-container-hidden]': 'hidden()',\n '[attr.aria-hidden]': 'hidden() || null',\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class ContainerFieldComponent {\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly injector = inject(Injector);\n protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n private readonly wrapperAutoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);\n private readonly defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });\n\n private readonly childrenTpl = viewChild.required('childrenTpl', { read: TemplateRef });\n private readonly wrapperContainer = viewChild.required('wrapperContainer', { read: ViewContainerRef });\n\n field = input.required<ContainerField>();\n key = input.required<string>();\n className = input<string>();\n hidden = input(false);\n\n readonly hostClasses = computed(() => computeContainerHostClasses('container', this.className()));\n readonly disabled = computed(() => this.field().disabled || false);\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n private readonly fieldsSource = computed(() => (this.field().fields || []) as FieldDef<unknown>[]);\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\n const containerKey = this.field().key || '<no key>';\n this.logger.error(\n `Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within container '${containerKey}'. 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 private readonly wrappers = computed(() => resolveWrappers(this.field(), this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), {\n equal: isSameWrapperChain,\n });\n\n constructor() {\n setupContainerInitEffect(this.resolvedFields, this.eventBus, 'container', () => this.field().key, this.injector);\n\n createWrapperChainController({\n vcr: this.wrapperContainer,\n wrappers: this.wrappers,\n renderInnermost: (slot) => slot.createEmbeddedView(this.childrenTpl()),\n });\n }\n}\n\nexport { ContainerFieldComponent };\n"]}
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, linkedSignal, runInInjectionContext, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { i as injectFieldRegistry, C as CONTAINER_FIELD_PROCESSORS, d as createSchemaFromFields, c as createFieldResolutionPipe, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-CUihkwGC.mjs';
3
+ import { i as injectFieldRegistry, C as CONTAINER_FIELD_PROCESSORS, d as createSchemaFromFields, c as createFieldResolutionPipe, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-BGRxIQ2V.mjs';
4
4
  import { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
5
5
  import { explicitEffect } from 'ngxtension/explicit-effect';
6
6
  import { injectFieldSignalContext, GROUP_CONTEXT, EventBus, DynamicFormLogger, isEqual, FIELD_SIGNAL_CONTEXT, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
@@ -178,4 +178,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
178
178
  }], 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 }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], validityChange: [{ type: i0.Output, args: ["validityChange"] }], dirtyChange: [{ type: i0.Output, args: ["dirtyChange"] }], submitted: [{ type: i0.Output, args: ["submitted"] }] } });
179
179
 
180
180
  export { GroupFieldComponent, GroupFieldComponent as default };
181
- //# sourceMappingURL=ng-forge-dynamic-forms-group-field.component-DA-FW30T.mjs.map
181
+ //# sourceMappingURL=ng-forge-dynamic-forms-group-field.component-DyD00a1U.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-forge-dynamic-forms-group-field.component-DA-FW30T.mjs","mappings":";;;;;;;;;AA0Dc;YAKK;;;;AAIA;AACE;AACF;AACA;YAMA;;;;;AAQjB;;;;YAOS;;;;AAIQ;AACf;AACA;AAEA;AACE;AACA;;AAEA;;AAGE;AACA;;;;;;;AAQF;AACA;AACA;AACA;;;;;;;AAOJ,IAKiB;;;AAIb;;AAEA;AACF;AAEE;AACA;AACD;AAGc;AACf;AACE;;AAGE;AACA;;AAGF;AACF;;;;;AAQO;;AACA;;AACA;;AACA;;AACA;;AACA;;AAEQ;AACf;;AAEA;;;;AAIA;;;AAGE;;AAEA;AACE;;;;AAKN,IAMiB;AACf;;;AAGE;AACF;;AAGe;;AAEf;;;AAGC;AACF;;;AAQQ;;;AAQU;AAGf;AACA;;;AAGA;AACE;;;;AAOH;YAQH;;;;;AAWE;;AAIM;;AAIJ;;;AA/Me;;;;;;AAhBlB;;;;AARS;;;;;;;AAWR;AACA;AACA;AACA;AACA;wEAIA;AACA","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/group/group-field.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n EnvironmentInjector,\n inject,\n Injector,\n input,\n linkedSignal,\n runInInjectionContext,\n untracked,\n} from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport { computeContainerHostClasses, setupContainerInitEffect } from '../../utils/container-utils/container-utils';\nimport { CONTAINER_FIELD_PROCESSORS } from '../../utils/container-utils/container-field-processors';\nimport { isEqual } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\nimport { GroupField } from '@ng-forge/dynamic-forms/internal';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { FieldTree, form } from '@angular/forms/signals';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { FieldSignalContext, GroupContext } from '@ng-forge/dynamic-forms/internal';\nimport { FIELD_SIGNAL_CONTEXT, GROUP_CONTEXT, injectFieldSignalContext } from '@ng-forge/dynamic-forms/internal';\nimport { createSchemaFromFields } from '../../core/schema-builder';\nimport { EventBus } from '@ng-forge/dynamic-forms/internal';\nimport { FormSubmitEvent } from '../../events/constants/submit.event';\n\n/** Container component for rendering nested form groups. */\n@Component({\n selector: 'fieldset[group-field]',\n imports: [DfFieldOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n @if (!field.hidden()) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n }\n `,\n styleUrl: './group-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n role: 'group',\n '[class.disabled]': 'disabled()',\n '[class.df-container-hidden]': 'hidden()',\n '[attr.aria-hidden]': 'hidden() || null',\n // The group element's own id is the plain `key()` — only its DESCENDANTS\n // compose scoped ids via GROUP_CONTEXT (e.g. children of an `address`\n // group render as `address_street`). The group selector is `#address`.\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class GroupFieldComponent<TModel extends Record<string, unknown> = Record<string, unknown>> {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly parentFieldSignalContext = injectFieldSignalContext<TModel>();\n private readonly parentGroupContext = inject(GROUP_CONTEXT, { optional: true });\n private readonly injector = inject(Injector);\n protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Memoized Functions\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<GroupField>();\n key = input.required<string>();\n className = input<string>();\n hidden = input(false);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly hostClasses = computed(() => computeContainerHostClasses('group', this.className()));\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n private readonly formSetup = computed(() => {\n const groupField = this.field();\n const registry = this.rawFieldRegistry();\n\n if (groupField.fields && groupField.fields.length > 0) {\n const flattenedFields = this.fieldProcessors.memoizedFlattenFields(groupField.fields, registry);\n const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(groupField.fields, registry);\n const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);\n const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);\n\n return {\n fields: flattenedFieldsForRendering,\n schemaFields: flattenedFields,\n originalFields: groupField.fields,\n defaultValues,\n registry,\n };\n }\n\n return {\n fields: [],\n schemaFields: [],\n originalFields: [],\n defaultValues: {},\n registry,\n };\n });\n\n readonly defaultValues = linkedSignal(() => this.formSetup().defaultValues);\n\n /**\n * Entity computed from parent value, group key, and defaults.\n * Uses deep equality check to prevent unnecessary updates when\n * object spread creates new references with identical values.\n */\n private readonly entity = linkedSignal(\n () => {\n const parentValue = this.parentFieldSignalContext.value();\n const groupKey = this.field().key;\n const defaults = this.defaultValues();\n const groupValue = (parentValue as Record<string, unknown>)?.[groupKey] || {};\n return { ...defaults, ...groupValue };\n },\n {\n debugName: 'GroupFieldComponent.entity',\n equal: isEqual,\n },\n );\n\n private readonly form = computed(() => {\n return runInInjectionContext(this.injector, () => {\n const setup = this.formSetup();\n\n if (setup.schemaFields.length > 0) {\n const schema = createSchemaFromFields(setup.schemaFields, setup.registry);\n return untracked(() => form(this.entity, schema));\n }\n\n return untracked(() => form(this.entity));\n });\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Public State Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly formValue = computed(() => this.entity());\n readonly valid = computed(() => this.form()().valid());\n readonly invalid = computed(() => this.form()().invalid());\n readonly dirty = computed(() => this.form()().dirty());\n readonly touched = computed(() => this.form()().touched());\n readonly errors = computed(() => this.form()().errors());\n readonly disabled = computed(() => this.form()().disabled());\n\n private readonly nestedFieldTree = computed((): FieldTree<Record<string, unknown>> | undefined => {\n const parentForm = this.parentFieldSignalContext.form as Record<string, FieldTree<Record<string, unknown>>>;\n const groupKey = this.field().key;\n return parentForm[groupKey];\n });\n\n private readonly groupFieldSignalContext: FieldSignalContext<Record<string, unknown>> = (() => {\n const nestedFieldTree = this.nestedFieldTree;\n return {\n injector: this.injector,\n value: this.parentFieldSignalContext.value,\n defaultValues: this.defaultValues,\n get form() {\n return nestedFieldTree() ?? ({} as FieldTree<Record<string, unknown>>);\n },\n };\n })();\n\n /**\n * Group context propagated to descendants — carries the dot-separated path of\n * group ancestors so child fields can scope their DOM IDs (issue #401).\n * Reactive via `computed()`: a nested group composes its path from the parent\n * context signal + its own key, so the chain stays consistent if either changes.\n */\n private readonly groupContext: GroupContext = {\n groupPath: computed(() => {\n const ownKey = this.field().key;\n const parentPath = this.parentGroupContext?.groupPath();\n return parentPath ? `${parentPath}.${ownKey}` : ownKey;\n }),\n };\n\n private readonly groupInjector = Injector.create({\n parent: this.injector,\n providers: [\n { provide: FIELD_SIGNAL_CONTEXT, useValue: this.groupFieldSignalContext },\n { provide: GROUP_CONTEXT, useValue: this.groupContext },\n ],\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly validityChange = outputFromObservable(toObservable(this.valid));\n readonly dirtyChange = outputFromObservable(toObservable(this.dirty));\n readonly submitted = outputFromObservable(this.eventBus.on<FormSubmitEvent>('submit'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => this.formSetup().fields);\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.groupInjector,\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 group '${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, 'group', () => this.field().key, this.injector);\n\n explicitEffect(\n [this.nestedFieldTree, this.field],\n ([tree, field]) => {\n if (!tree) {\n this.logger.warn(\n `Group field \"${field.key}\" not found in parent form. ` + `Ensure the parent form schema includes this group field.`,\n );\n }\n },\n { defer: true },\n );\n }\n}\n\nexport { GroupFieldComponent };\n"]}
1
+ {"version":3,"file":"ng-forge-dynamic-forms-group-field.component-DyD00a1U.mjs","mappings":";;;;;;;;;AA0Dc;YAKK;;;;AAIA;AACE;AACF;AACA;YAMA;;;;;AAQjB;;;;YAOS;;;;AAIQ;AACf;AACA;AAEA;AACE;AACA;;AAEA;;AAGE;AACA;;;;;;;AAQF;AACA;AACA;AACA;;;;;;;AAOJ,IAKiB;;;AAIb;;AAEA;AACF;AAEE;AACA;AACD;AAGc;AACf;AACE;;AAGE;AACA;;AAGF;AACF;;;;;AAQO;;AACA;;AACA;;AACA;;AACA;;AACA;;AAEQ;AACf;;AAEA;;;;AAIA;;;AAGE;;AAEA;AACE;;;;AAKN,IAMiB;AACf;;;AAGE;AACF;;AAGe;;AAEf;;;AAGC;AACF;;;AAQQ;;;AAQU;AAGf;AACA;;;AAGA;AACE;;;;AAOH;YAQH;;;;;AAWE;;AAIM;;AAIJ;;;AA/Me;;;;;;AAhBlB;;;;AARS;;;;;;;AAWR;AACA;AACA;AACA;AACA;wEAIA;AACA","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/group/group-field.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n EnvironmentInjector,\n inject,\n Injector,\n input,\n linkedSignal,\n runInInjectionContext,\n untracked,\n} from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport { computeContainerHostClasses, setupContainerInitEffect } from '../../utils/container-utils/container-utils';\nimport { CONTAINER_FIELD_PROCESSORS } from '../../utils/container-utils/container-field-processors';\nimport { isEqual } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\nimport { GroupField } from '@ng-forge/dynamic-forms/internal';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { FieldTree, form } from '@angular/forms/signals';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { FieldSignalContext, GroupContext } from '@ng-forge/dynamic-forms/internal';\nimport { FIELD_SIGNAL_CONTEXT, GROUP_CONTEXT, injectFieldSignalContext } from '@ng-forge/dynamic-forms/internal';\nimport { createSchemaFromFields } from '../../core/schema-builder';\nimport { EventBus } from '@ng-forge/dynamic-forms/internal';\nimport { FormSubmitEvent } from '../../events/constants/submit.event';\n\n/** Container component for rendering nested form groups. */\n@Component({\n selector: 'fieldset[group-field]',\n imports: [DfFieldOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n @if (!field.hidden()) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n }\n `,\n styleUrl: './group-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n role: 'group',\n '[class.disabled]': 'disabled()',\n '[class.df-container-hidden]': 'hidden()',\n '[attr.aria-hidden]': 'hidden() || null',\n // The group element's own id is the plain `key()` — only its DESCENDANTS\n // compose scoped ids via GROUP_CONTEXT (e.g. children of an `address`\n // group render as `address_street`). The group selector is `#address`.\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class GroupFieldComponent<TModel extends Record<string, unknown> = Record<string, unknown>> {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly parentFieldSignalContext = injectFieldSignalContext<TModel>();\n private readonly parentGroupContext = inject(GROUP_CONTEXT, { optional: true });\n private readonly injector = inject(Injector);\n protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Memoized Functions\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<GroupField>();\n key = input.required<string>();\n className = input<string>();\n hidden = input(false);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly hostClasses = computed(() => computeContainerHostClasses('group', this.className()));\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n private readonly formSetup = computed(() => {\n const groupField = this.field();\n const registry = this.rawFieldRegistry();\n\n if (groupField.fields && groupField.fields.length > 0) {\n const flattenedFields = this.fieldProcessors.memoizedFlattenFields(groupField.fields, registry);\n const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(groupField.fields, registry);\n const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);\n const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);\n\n return {\n fields: flattenedFieldsForRendering,\n schemaFields: flattenedFields,\n originalFields: groupField.fields,\n defaultValues,\n registry,\n };\n }\n\n return {\n fields: [],\n schemaFields: [],\n originalFields: [],\n defaultValues: {},\n registry,\n };\n });\n\n readonly defaultValues = linkedSignal(() => this.formSetup().defaultValues);\n\n /**\n * Entity computed from parent value, group key, and defaults.\n * Uses deep equality check to prevent unnecessary updates when\n * object spread creates new references with identical values.\n */\n private readonly entity = linkedSignal(\n () => {\n const parentValue = this.parentFieldSignalContext.value();\n const groupKey = this.field().key;\n const defaults = this.defaultValues();\n const groupValue = (parentValue as Record<string, unknown>)?.[groupKey] || {};\n return { ...defaults, ...groupValue };\n },\n {\n debugName: 'GroupFieldComponent.entity',\n equal: isEqual,\n },\n );\n\n private readonly form = computed(() => {\n return runInInjectionContext(this.injector, () => {\n const setup = this.formSetup();\n\n if (setup.schemaFields.length > 0) {\n const schema = createSchemaFromFields(setup.schemaFields, setup.registry);\n return untracked(() => form(this.entity, schema));\n }\n\n return untracked(() => form(this.entity));\n });\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Public State Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly formValue = computed(() => this.entity());\n readonly valid = computed(() => this.form()().valid());\n readonly invalid = computed(() => this.form()().invalid());\n readonly dirty = computed(() => this.form()().dirty());\n readonly touched = computed(() => this.form()().touched());\n readonly errors = computed(() => this.form()().errors());\n readonly disabled = computed(() => this.form()().disabled());\n\n private readonly nestedFieldTree = computed((): FieldTree<Record<string, unknown>> | undefined => {\n const parentForm = this.parentFieldSignalContext.form as Record<string, FieldTree<Record<string, unknown>>>;\n const groupKey = this.field().key;\n return parentForm[groupKey];\n });\n\n private readonly groupFieldSignalContext: FieldSignalContext<Record<string, unknown>> = (() => {\n const nestedFieldTree = this.nestedFieldTree;\n return {\n injector: this.injector,\n value: this.parentFieldSignalContext.value,\n defaultValues: this.defaultValues,\n get form() {\n return nestedFieldTree() ?? ({} as FieldTree<Record<string, unknown>>);\n },\n };\n })();\n\n /**\n * Group context propagated to descendants — carries the dot-separated path of\n * group ancestors so child fields can scope their DOM IDs (issue #401).\n * Reactive via `computed()`: a nested group composes its path from the parent\n * context signal + its own key, so the chain stays consistent if either changes.\n */\n private readonly groupContext: GroupContext = {\n groupPath: computed(() => {\n const ownKey = this.field().key;\n const parentPath = this.parentGroupContext?.groupPath();\n return parentPath ? `${parentPath}.${ownKey}` : ownKey;\n }),\n };\n\n private readonly groupInjector = Injector.create({\n parent: this.injector,\n providers: [\n { provide: FIELD_SIGNAL_CONTEXT, useValue: this.groupFieldSignalContext },\n { provide: GROUP_CONTEXT, useValue: this.groupContext },\n ],\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly validityChange = outputFromObservable(toObservable(this.valid));\n readonly dirtyChange = outputFromObservable(toObservable(this.dirty));\n readonly submitted = outputFromObservable(this.eventBus.on<FormSubmitEvent>('submit'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => this.formSetup().fields);\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.groupInjector,\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 group '${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, 'group', () => this.field().key, this.injector);\n\n explicitEffect(\n [this.nestedFieldTree, this.field],\n ([tree, field]) => {\n if (!tree) {\n this.logger.warn(\n `Group field \"${field.key}\" not found in parent form. ` + `Ensure the parent form schema includes this group field.`,\n );\n }\n },\n { defer: true },\n );\n }\n}\n\nexport { GroupFieldComponent };\n"]}
@@ -437,10 +437,6 @@ class FormSubmitEvent {
437
437
  type = 'submit';
438
438
  }
439
439
 
440
- class NextPageEvent {
441
- type = 'next-page';
442
- }
443
-
444
440
  class PageChangeEvent {
445
441
  currentPageIndex;
446
442
  totalPages;
@@ -456,105 +452,9 @@ class PageChangeEvent {
456
452
  }
457
453
  }
458
454
 
459
- class PreviousPageEvent {
460
- type = 'previous-page';
461
- }
462
-
463
- class FormResetEvent {
464
- type = 'form-reset';
465
- }
466
-
467
- class FormClearEvent {
468
- type = 'form-clear';
469
- }
470
-
471
- class AppendArrayItemEvent {
472
- arrayKey;
473
- template;
474
- type = 'append-array-item';
475
- constructor(
476
- arrayKey,
477
- template) {
478
- this.arrayKey = arrayKey;
479
- this.template = template;
480
- }
481
- }
482
-
483
- class PrependArrayItemEvent {
484
- arrayKey;
485
- template;
486
- type = 'prepend-array-item';
487
- constructor(
488
- arrayKey,
489
- template) {
490
- this.arrayKey = arrayKey;
491
- this.template = template;
492
- }
493
- }
494
-
495
- class InsertArrayItemEvent {
496
- arrayKey;
497
- index;
498
- template;
499
- type = 'insert-array-item';
500
- constructor(
501
- arrayKey,
502
- index,
503
- template) {
504
- this.arrayKey = arrayKey;
505
- this.index = index;
506
- this.template = template;
507
- }
508
- }
509
-
510
- class PopArrayItemEvent {
511
- arrayKey;
512
- type = 'pop-array-item';
513
- constructor(
514
- arrayKey) {
515
- this.arrayKey = arrayKey;
516
- }
517
- }
518
-
519
- class ShiftArrayItemEvent {
520
- arrayKey;
521
- type = 'shift-array-item';
522
- constructor(
523
- arrayKey) {
524
- this.arrayKey = arrayKey;
525
- }
526
- }
527
-
528
- class MoveArrayItemEvent {
529
- arrayKey;
530
- fromIndex;
531
- toIndex;
532
- type = 'move-array-item';
533
- constructor(
534
- arrayKey,
535
- fromIndex,
536
- toIndex) {
537
- this.arrayKey = arrayKey;
538
- this.fromIndex = fromIndex;
539
- this.toIndex = toIndex;
540
- }
541
- }
542
-
543
- class RemoveAtIndexEvent {
544
- arrayKey;
545
- index;
546
- type = 'remove-at-index';
547
- constructor(
548
- arrayKey,
549
- index) {
550
- this.arrayKey = arrayKey;
551
- this.index = index;
552
- }
553
- }
554
-
555
- class PageNavigationStateChangeEvent {
455
+ class PagerStateEvent {
556
456
  state;
557
- type = 'page-navigation-state-change';
457
+ type = 'pager-state';
558
458
  constructor(state) {
559
459
  this.state = state;
560
460
  }
@@ -615,20 +515,18 @@ class PageOrchestratorComponent {
615
515
  totalPages,
616
516
  isFirstPage: isFirstVisiblePage,
617
517
  isLastPage: isLastVisiblePage,
618
- navigationDisabled: false,
619
518
  };
620
519
  }, /* @ts-ignore */
621
520
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
622
- currentPageValid = computed(() => {
623
- const currentIndex = this.currentPageIndex();
521
+ currentPageValid = computed(() => this.isPageValid(this.currentPageIndex()), /* @ts-ignore */
522
+ ...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
523
+ isPageValid(pageIndex) {
624
524
  const pages = this.pageFields();
625
525
  const form = this.form();
626
- if (pages.length === 0 || currentIndex >= pages.length) {
526
+ if (pageIndex < 0 || pageIndex >= pages.length) {
627
527
  return true;
628
528
  }
629
- const currentPage = pages[currentIndex];
630
- const pageFields = currentPage.fields || [];
631
- const leafKeys = collectLeafFieldKeys(pageFields);
529
+ const leafKeys = collectLeafFieldKeys(pages[pageIndex].fields || []);
632
530
  for (const fieldKey of leafKeys) {
633
531
  const field = form[fieldKey];
634
532
  if (field && typeof field === 'function') {
@@ -639,8 +537,7 @@ class PageOrchestratorComponent {
639
537
  }
640
538
  }
641
539
  return true;
642
- }, /* @ts-ignore */
643
- ...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
540
+ }
644
541
  extendedFieldSignalContext = computed(() => ({
645
542
  ...this.fieldSignalContext(),
646
543
  currentPageValid: this.currentPageValid,
@@ -661,7 +558,7 @@ class PageOrchestratorComponent {
661
558
  if (currentVisiblePosition === -1 && visibleIndices.length > 0) {
662
559
  const nearest = this.findNearestVisiblePage(state.currentPageIndex, visibleIndices);
663
560
  if (nearest !== -1) {
664
- this.navigateToPage(nearest);
561
+ this.executePageChange(nearest);
665
562
  }
666
563
  }
667
564
  });
@@ -683,13 +580,6 @@ class PageOrchestratorComponent {
683
580
  newPageIndex: currentState.currentPageIndex,
684
581
  error: 'Already on the last visible page',
685
582
  };
686
- }
687
- if (currentState.navigationDisabled) {
688
- return {
689
- success: false,
690
- newPageIndex: currentState.currentPageIndex,
691
- error: 'Navigation is currently disabled',
692
- };
693
583
  }
694
584
  const currentVisiblePosition = visibleIndices.indexOf(currentState.currentPageIndex);
695
585
  if (currentVisiblePosition === -1 || currentVisiblePosition >= visibleIndices.length - 1) {
@@ -700,7 +590,7 @@ class PageOrchestratorComponent {
700
590
  };
701
591
  }
702
592
  const nextVisiblePageIndex = visibleIndices[currentVisiblePosition + 1];
703
- return this.navigateToPage(nextVisiblePageIndex);
593
+ return this.executePageChange(nextVisiblePageIndex);
704
594
  }
705
595
  navigateToPreviousPage() {
706
596
  const currentState = this.state();
@@ -711,13 +601,6 @@ class PageOrchestratorComponent {
711
601
  newPageIndex: currentState.currentPageIndex,
712
602
  error: 'Already on the first visible page',
713
603
  };
714
- }
715
- if (currentState.navigationDisabled) {
716
- return {
717
- success: false,
718
- newPageIndex: currentState.currentPageIndex,
719
- error: 'Navigation is currently disabled',
720
- };
721
604
  }
722
605
  const currentVisiblePosition = visibleIndices.indexOf(currentState.currentPageIndex);
723
606
  if (currentVisiblePosition <= 0) {
@@ -728,9 +611,31 @@ class PageOrchestratorComponent {
728
611
  };
729
612
  }
730
613
  const prevVisiblePageIndex = visibleIndices[currentVisiblePosition - 1];
731
- return this.navigateToPage(prevVisiblePageIndex);
614
+ return this.executePageChange(prevVisiblePageIndex);
732
615
  }
733
616
  navigateToPage(pageIndex) {
617
+ const currentIndex = this.state().currentPageIndex;
618
+ const visibleIndices = this.visiblePageIndices();
619
+ if (pageIndex <= currentIndex || !visibleIndices.includes(pageIndex)) {
620
+ return this.executePageChange(pageIndex);
621
+ }
622
+ const disableWhenPageInvalid = this.formOptions?.()?.nextButton?.disableWhenPageInvalid ?? true;
623
+ if (!disableWhenPageInvalid) {
624
+ return this.executePageChange(pageIndex);
625
+ }
626
+ const crossedPages = visibleIndices.filter((index) => index >= currentIndex && index < pageIndex);
627
+ const firstInvalidPage = crossedPages.find((index) => !this.isPageValid(index));
628
+ if (firstInvalidPage === undefined) {
629
+ return this.executePageChange(pageIndex);
630
+ }
631
+ this.executePageChange(firstInvalidPage);
632
+ return {
633
+ success: false,
634
+ newPageIndex: firstInvalidPage,
635
+ error: `Cannot navigate to page ${pageIndex}: page ${firstInvalidPage} has invalid fields`,
636
+ };
637
+ }
638
+ executePageChange(pageIndex) {
734
639
  const currentState = this.state();
735
640
  const totalPages = currentState.totalPages;
736
641
  if (pageIndex < 0 || pageIndex >= totalPages) {
@@ -787,7 +692,13 @@ class PageOrchestratorComponent {
787
692
  .subscribe(() => {
788
693
  this.navigateToPreviousPage();
789
694
  });
790
- explicitEffect([this.state], ([state]) => this.eventBus.dispatch(PageNavigationStateChangeEvent, state));
695
+ this.eventBus
696
+ .on('go-to-page')
697
+ .pipe(takeUntilDestroyed())
698
+ .subscribe((event) => {
699
+ this.navigateToPage(event.pageIndex);
700
+ });
701
+ explicitEffect([this.state], ([state]) => this.eventBus.dispatch(PagerStateEvent, state));
791
702
  }
792
703
  evaluatePageHidden(page) {
793
704
  if (!page.logic || page.logic.length === 0) {
@@ -863,10 +774,10 @@ class PageOrchestratorComponent {
863
774
  }
864
775
  }
865
776
  `, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
866
- import('./ng-forge-dynamic-forms-page-field.component-P9O4eNvS.mjs').then(m => m.default)]] });
777
+ import('./ng-forge-dynamic-forms-page-field.component-B7A2v1vN.mjs').then(m => m.default)]] });
867
778
  }
868
779
  i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.1", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [/* @ts-ignore */
869
- import('./ng-forge-dynamic-forms-page-field.component-P9O4eNvS.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
780
+ import('./ng-forge-dynamic-forms-page-field.component-B7A2v1vN.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
870
781
  type: Component,
871
782
  args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
872
783
  @for (pageField of pageFields(); track pageField.key; let i = $index) {
@@ -3880,7 +3791,7 @@ function createDerivationRenderGate() {
3880
3791
  if (!has || engine() !== 'idle')
3881
3792
  return;
3882
3793
  engine.set('loading');
3883
- pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-VJBOH393.mjs')
3794
+ pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-jHiY95pr.mjs')
3884
3795
  .then(({ bootstrapDerivationOrchestrator }) => {
3885
3796
  runInInjectionContext(injector, () => bootstrapDerivationOrchestrator());
3886
3797
  engine.set('wired');
@@ -4211,7 +4122,7 @@ class DynamicForm {
4211
4122
  events = outputFromObservable(this.eventBus.events$);
4212
4123
  initialized = outputFromObservable(this.initialized$);
4213
4124
  onPageChange = outputFromObservable(this.eventBus.on('page-change'));
4214
- onPageNavigationStateChange = outputFromObservable(this.eventBus.on('page-navigation-state-change'));
4125
+ onPageNavigationStateChange = outputFromObservable(this.eventBus.on('pager-state'));
4215
4126
  constructor() {
4216
4127
  this.dispatcher?.connect(this.eventBus);
4217
4128
  this.destroyRef.onDestroy(() => this.dispatcher?.disconnect());
@@ -4435,28 +4346,28 @@ const DISPLAY_FIELD_TYPES_BASE = {
4435
4346
  const BUILT_IN_FIELDS = [
4436
4347
  {
4437
4348
  name: 'row',
4438
- loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-o49q6akV.mjs'),
4349
+ loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-ug5tp6kt.mjs'),
4439
4350
  mapper: rowFieldMapper,
4440
4351
  valueHandling: 'flatten',
4441
4352
  ...CONTAINER_FIELD_TYPES_BASE,
4442
4353
  },
4443
4354
  {
4444
4355
  name: 'group',
4445
- loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-DA-FW30T.mjs'),
4356
+ loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-DyD00a1U.mjs'),
4446
4357
  mapper: groupFieldMapper,
4447
4358
  valueHandling: 'include',
4448
4359
  ...CONTAINER_FIELD_TYPES_BASE,
4449
4360
  },
4450
4361
  {
4451
4362
  name: 'array',
4452
- loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-B9OfjKPI.mjs'),
4363
+ loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-BLPWSurR.mjs'),
4453
4364
  mapper: arrayFieldMapper,
4454
4365
  valueHandling: 'include',
4455
4366
  ...CONTAINER_FIELD_TYPES_BASE,
4456
4367
  },
4457
4368
  {
4458
4369
  name: 'page',
4459
- loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-P9O4eNvS.mjs'),
4370
+ loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-B7A2v1vN.mjs'),
4460
4371
  mapper: pageFieldMapper,
4461
4372
  valueHandling: 'flatten',
4462
4373
  ...CONTAINER_FIELD_TYPES_BASE,
@@ -4474,7 +4385,7 @@ const BUILT_IN_FIELDS = [
4474
4385
  },
4475
4386
  {
4476
4387
  name: 'container',
4477
- loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-o49q6akV.mjs'),
4388
+ loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-ug5tp6kt.mjs'),
4478
4389
  mapper: containerFieldMapper,
4479
4390
  valueHandling: 'flatten',
4480
4391
  ...CONTAINER_FIELD_TYPES_BASE,
@@ -4876,6 +4787,115 @@ function sanitizeFormConfigPure(config, fieldRegistry, typeRegistry, options = {
4876
4787
  return { sanitized: { ...config, fields: fields }, warnings };
4877
4788
  }
4878
4789
 
4790
+ class GoToPageEvent {
4791
+ pageIndex;
4792
+ type = 'go-to-page';
4793
+ constructor(
4794
+ pageIndex) {
4795
+ this.pageIndex = pageIndex;
4796
+ }
4797
+ }
4798
+
4799
+ class NextPageEvent {
4800
+ type = 'next-page';
4801
+ }
4802
+
4803
+ class PreviousPageEvent {
4804
+ type = 'previous-page';
4805
+ }
4806
+
4807
+ class FormResetEvent {
4808
+ type = 'form-reset';
4809
+ }
4810
+
4811
+ class FormClearEvent {
4812
+ type = 'form-clear';
4813
+ }
4814
+
4815
+ class AppendArrayItemEvent {
4816
+ arrayKey;
4817
+ template;
4818
+ type = 'append-array-item';
4819
+ constructor(
4820
+ arrayKey,
4821
+ template) {
4822
+ this.arrayKey = arrayKey;
4823
+ this.template = template;
4824
+ }
4825
+ }
4826
+
4827
+ class PrependArrayItemEvent {
4828
+ arrayKey;
4829
+ template;
4830
+ type = 'prepend-array-item';
4831
+ constructor(
4832
+ arrayKey,
4833
+ template) {
4834
+ this.arrayKey = arrayKey;
4835
+ this.template = template;
4836
+ }
4837
+ }
4838
+
4839
+ class InsertArrayItemEvent {
4840
+ arrayKey;
4841
+ index;
4842
+ template;
4843
+ type = 'insert-array-item';
4844
+ constructor(
4845
+ arrayKey,
4846
+ index,
4847
+ template) {
4848
+ this.arrayKey = arrayKey;
4849
+ this.index = index;
4850
+ this.template = template;
4851
+ }
4852
+ }
4853
+
4854
+ class PopArrayItemEvent {
4855
+ arrayKey;
4856
+ type = 'pop-array-item';
4857
+ constructor(
4858
+ arrayKey) {
4859
+ this.arrayKey = arrayKey;
4860
+ }
4861
+ }
4862
+
4863
+ class ShiftArrayItemEvent {
4864
+ arrayKey;
4865
+ type = 'shift-array-item';
4866
+ constructor(
4867
+ arrayKey) {
4868
+ this.arrayKey = arrayKey;
4869
+ }
4870
+ }
4871
+
4872
+ class MoveArrayItemEvent {
4873
+ arrayKey;
4874
+ fromIndex;
4875
+ toIndex;
4876
+ type = 'move-array-item';
4877
+ constructor(
4878
+ arrayKey,
4879
+ fromIndex,
4880
+ toIndex) {
4881
+ this.arrayKey = arrayKey;
4882
+ this.fromIndex = fromIndex;
4883
+ this.toIndex = toIndex;
4884
+ }
4885
+ }
4886
+
4887
+ class RemoveAtIndexEvent {
4888
+ arrayKey;
4889
+ index;
4890
+ type = 'remove-at-index';
4891
+ constructor(
4892
+ arrayKey,
4893
+ index) {
4894
+ this.arrayKey = arrayKey;
4895
+ this.index = index;
4896
+ }
4897
+ }
4898
+
4879
4899
  function arrayEvent(arrayKey) {
4880
4900
  return {
4881
4901
  append: (template) => new AppendArrayItemEvent(arrayKey, template),
@@ -4891,5 +4911,5 @@ function arrayEvent(arrayKey) {
4891
4911
 
4892
4912
 
4893
4913
 
4894
- export { withLegacyStatusClasses as $, ArrayItemRegistryService as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfFieldOutlet as D, EventDispatcher as E, FIELD_WINDOWING as F, PageChangeEvent as G, PopArrayItemEvent as H, InsertArrayItemEvent as I, PrependArrayItemEvent as J, PreviousPageEvent as K, TextAddonComponent as L, MoveArrayItemEvent as M, NextPageEvent as N, arrayEvent as O, PROPERTY_OVERRIDE_STORE as P, createWrappers as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, isWrappersBundle as U, provideDynamicForm as V, sanitizeFormConfig as W, withAddonActions as X, withCustomAddon as Y, withEventFormValue as Z, withFieldWindowing as _, isSameWrapperChain as a, withLoggerConfig as a0, withPagePreload as a1, withValidationExecutionDefaults as a2, withValueExclusionDefaults as a3, wrapperProps as a4, createWrapperChainController as b, createFieldResolutionPipe as c, createSchemaFromFields as d, createHiddenSignal as e, createRenderReadySignal as f, getNormalizedArrayMetadata as g, getFieldDefaultValue as h, injectFieldRegistry as i, resolveFieldWindowing as j, DEV_MODE as k, DEFAULT_DEBOUNCE_MS as l, mapFieldToInputs as m, DERIVATION_WARNING_TRACKER as n, FormStateManager as o, DERIVATION_LOG_CONFIG as p, AppendArrayItemEvent as q, resolveWrappers as r, ComponentAddonComponent as s, traverseFieldsWithContext as t, ConsoleLogger as u, DfTemplate as v, DynamicForm as w, FormClearEvent as x, FormResetEvent as y, FormSubmitEvent as z };
4895
- //# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-CUihkwGC.mjs.map
4914
+ export { withEventFormValue as $, ArrayItemRegistryService as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfFieldOutlet as D, EventDispatcher as E, FIELD_WINDOWING as F, GoToPageEvent as G, PageChangeEvent as H, InsertArrayItemEvent as I, PagerStateEvent as J, PopArrayItemEvent as K, PrependArrayItemEvent as L, MoveArrayItemEvent as M, NextPageEvent as N, PreviousPageEvent as O, PROPERTY_OVERRIDE_STORE as P, TextAddonComponent as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, arrayEvent as U, createWrappers as V, isWrappersBundle as W, provideDynamicForm as X, sanitizeFormConfig as Y, withAddonActions as Z, withCustomAddon as _, isSameWrapperChain as a, withFieldWindowing as a0, withLegacyStatusClasses as a1, withLoggerConfig as a2, withPagePreload as a3, withValidationExecutionDefaults as a4, withValueExclusionDefaults as a5, wrapperProps as a6, createWrapperChainController as b, createFieldResolutionPipe as c, createSchemaFromFields as d, createHiddenSignal as e, createRenderReadySignal as f, getNormalizedArrayMetadata as g, getFieldDefaultValue as h, injectFieldRegistry as i, resolveFieldWindowing as j, DEV_MODE as k, DEFAULT_DEBOUNCE_MS as l, mapFieldToInputs as m, DERIVATION_WARNING_TRACKER as n, FormStateManager as o, DERIVATION_LOG_CONFIG as p, AppendArrayItemEvent as q, resolveWrappers as r, ComponentAddonComponent as s, traverseFieldsWithContext as t, ConsoleLogger as u, DfTemplate as v, DynamicForm as w, FormClearEvent as x, FormResetEvent as y, FormSubmitEvent as z };
4915
+ //# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-BGRxIQ2V.mjs.map