🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@ng-forge/dynamic-forms

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ng-forge/dynamic-forms - npm Package Compare versions

Comparing version
1.1.0
to
1.2.0-next.1
+754
fesm2022/ng-forge-...orms-array-field.component-BVnXnaNw.mjs
import * as i0 from '@angular/core';
import { signal, computed, runInInjectionContext, Injector, linkedSignal, inject, DestroyRef, EnvironmentInjector, input, ChangeDetectionStrategy, Component } from '@angular/core';
import { m as mapFieldToInputs, e as createHiddenSignal, f as createRenderReadySignal, i as injectFieldRegistry, A as ArrayItemRegistryService, g as getNormalizedArrayMetadata, h as getFieldDefaultValue, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-ZiNaCWHl.mjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { of, from, map, catchError, forkJoin, filter, firstValueFrom, tap } from 'rxjs';
import { explicitEffect } from 'ngxtension/explicit-effect';
import { isEqual, FIELD_SIGNAL_CONTEXT, ARRAY_CONTEXT, GROUP_CONTEXT, DynamicFormLogger, injectFieldSignalContext, EventBus, getFieldValueHandling, isGroupField } from '@ng-forge/dynamic-forms/internal';
import { e as emitComponentInitialized } from './ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs';
class ArrayFieldStateMachine {
_state = signal('idle', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
_runId = signal(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_runId" }] : /* istanbul ignore next */ []));
lastEmittedRunId = -1;
_hasEverEmitted = false;
state = this._state.asReadonly();
runId = this._runId.asReadonly();
get hasEverEmitted() {
return this._hasEverEmitted;
}
dispatch(op) {
const nextId = this._runId() + 1;
this._runId.set(nextId);
switch (op.kind) {
case 'clear':
case 'initial':
case 'recreate':
this._state.set('pending');
break;
case 'append':
break;
}
return {
id: nextId,
isStale: () => this._runId() !== nextId,
resolve: () => {
if (this._runId() === nextId && this._state() === 'pending') {
this._state.set('settled');
}
},
};
}
shouldEmit(allReady) {
if (this._state() !== 'settled' || !allReady)
return false;
if (this.lastEmittedRunId === this._runId())
return false;
this.lastEmittedRunId = this._runId();
this._hasEverEmitted = true;
this._state.set('idle');
return true;
}
}
function getArrayValue(value, key) {
const arrayValue = value?.[key];
return Array.isArray(arrayValue) ? arrayValue : [];
}
function determineDifferentialOperation(currentItems, newLength) {
const currentLength = currentItems.length;
if (newLength === 0) {
return { type: 'clear' };
}
if (currentLength === 0) {
return { type: 'initial', fieldTreesLength: newLength };
}
if (newLength > currentLength) {
return { type: 'append', startIndex: currentLength, endIndex: newLength };
}
if (newLength < currentLength) {
return { type: 'recreate' };
}
return { type: 'none' };
}
function createArrayItemInjectorAndInputs(options) {
const { template, indexSignal, parentFieldSignalContext, parentInjector, registry, arrayField, primitiveFieldKey } = options;
const itemFormAccessor = computed(() => {
const parentForm = parentFieldSignalContext.form;
const index = indexSignal();
const arrayFieldTree = parentForm[arrayField.key];
if (!arrayFieldTree) {
return undefined;
}
return arrayFieldTree[index];
}, { ...(ngDevMode ? { debugName: "itemFormAccessor" } : /* istanbul ignore next */ {}), equal: isEqual });
const injector = createItemInjector({
itemFormAccessor,
indexSignal,
parentFieldSignalContext,
parentInjector,
arrayField,
primitiveFieldKey,
});
const inputs = runInInjectionContext(injector, () => {
return mapFieldToInputs(template, registry);
});
return { injector, inputs };
}
function createItemInjector(options) {
const { itemFormAccessor, indexSignal, parentFieldSignalContext, parentInjector, arrayField, primitiveFieldKey } = options;
const arrayContext = {
arrayKey: arrayField.key,
index: indexSignal,
get formValue() {
return parentFieldSignalContext.value();
},
field: arrayField,
};
let cachedPrimitiveRaw;
let cachedPrimitiveWrapped;
return Injector.create({
parent: parentInjector,
providers: [
{
provide: FIELD_SIGNAL_CONTEXT,
useFactory: (injector) => ({
injector,
value: parentFieldSignalContext.value,
defaultValues: () => ({}),
get form() {
const raw = itemFormAccessor();
if (!raw) {
return {};
}
if (primitiveFieldKey) {
if (raw !== cachedPrimitiveRaw) {
cachedPrimitiveRaw = raw;
cachedPrimitiveWrapped = { [primitiveFieldKey]: raw };
}
return cachedPrimitiveWrapped;
}
return raw;
},
}),
deps: [Injector],
},
{ provide: ARRAY_CONTEXT, useValue: arrayContext },
{ provide: GROUP_CONTEXT, useValue: EMPTY_GROUP_CONTEXT },
],
});
}
const EMPTY_GROUP_CONTEXT = { groupPath: signal('') };
function resolveArrayItem(options) {
const { index, templates, arrayField, itemPositionMap, parentFieldSignalContext, parentInjector, registry, destroyRef, loadTypeComponent, generateItemId, existingItemId, primitiveFieldKey, } = options;
if (templates.length === 0) {
return of(undefined);
}
const itemId = existingItemId ?? generateItemId();
const indexSignal = linkedSignal(() => {
const positionMap = itemPositionMap();
return positionMap.get(itemId) ?? index;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
const fieldObservables = templates.map((template) => from(loadTypeComponent(template.type)).pipe(map((component) => {
if (destroyRef.destroyed) {
return undefined;
}
if (!component) {
return undefined;
}
const { injector, inputs } = createArrayItemInjectorAndInputs({
template,
indexSignal,
parentFieldSignalContext,
parentInjector,
registry,
arrayField,
primitiveFieldKey,
});
if (!inputs) {
return undefined;
}
return {
key: template.key,
fieldDef: template,
component,
injector,
inputs,
renderReady: createRenderReadySignal(inputs, registry.get(template.type)),
hidden: createHiddenSignal(inputs),
};
}), catchError((error) => {
if (!destroyRef.destroyed) {
const logger = parentInjector.get(DynamicFormLogger);
logger.error(`Failed to load component for field type '${template.type}' at index ${index} ` +
`within array '${arrayField.key}'. Ensure the field type is registered in your field registry.`, error);
}
return of(undefined);
})));
return forkJoin(fieldObservables).pipe(map((fields) => {
const validFields = fields.filter((f) => f !== undefined);
if (validFields.length === 0) {
return undefined;
}
return {
id: itemId,
fields: validFields,
};
}));
}
const ARRAY_EVENT_TYPES = [
'append-array-item',
'prepend-array-item',
'insert-array-item',
'move-array-item',
'pop-array-item',
'shift-array-item',
'remove-at-index',
];
function toArrayAction(event) {
switch (event.type) {
case 'append-array-item':
return { action: 'add', template: event.template };
case 'prepend-array-item':
return { action: 'add', template: event.template, index: 0 };
case 'insert-array-item':
return { action: 'add', template: event.template, index: event.index };
case 'move-array-item':
return { action: 'move', fromIndex: event.fromIndex, toIndex: event.toIndex };
case 'pop-array-item':
return { action: 'remove' };
case 'shift-array-item':
return { action: 'remove', index: 0 };
case 'remove-at-index':
return { action: 'remove', index: event.index };
}
}
function observeArrayActions(eventBus, arrayKey) {
return eventBus.on(ARRAY_EVENT_TYPES).pipe(filter((event) => event.arrayKey === arrayKey()), map(toArrayAction));
}
class ArrayFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
parentFieldSignalContext = injectFieldSignalContext();
parentInjector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
arrayItemRegistry = inject(ArrayItemRegistryService);
slot = computed(() => this.arrayItemRegistry.slotFor(this.field().key), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "slot" }] : /* istanbul ignore next */ []));
generateItemId = () => this.slot().nextId();
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
hidden = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => {
const base = 'df-field df-array';
const custom = this.className();
return custom ? `${base} ${custom}` : base;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
autoRemoveButton = computed(() => {
const metadata = getNormalizedArrayMetadata(this.field());
return metadata?.autoRemoveButton;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "autoRemoveButton" }] : /* istanbul ignore next */ []));
primitiveFieldKey = linkedSignal(() => {
const metadata = getNormalizedArrayMetadata(this.field());
if (metadata?.primitiveFieldKey) {
return metadata.primitiveFieldKey;
}
const definitions = this.field().fields || [];
if (definitions.length > 0 && !Array.isArray(definitions[0])) {
return definitions[0].key;
}
return undefined;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
rawItemTemplates = computed(() => {
const arrayField = this.field();
const definitions = arrayField.fields || [];
return definitions.map((def) => {
return (Array.isArray(def) ? def : [def]);
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawItemTemplates" }] : /* istanbul ignore next */ []));
fallbackTemplate = computed(() => {
const raw = getNormalizedArrayMetadata(this.field())?.template;
if (!raw)
return undefined;
return (Array.isArray(raw) ? [...raw] : [raw]);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fallbackTemplate" }] : /* istanbul ignore next */ []));
itemTemplates = computed(() => {
const raw = this.rawItemTemplates();
const removeButton = this.autoRemoveButton();
if (!removeButton)
return raw;
return raw.map((normalized) => {
return [...normalized, removeButton];
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "itemTemplates" }] : /* istanbul ignore next */ []));
arrayFieldTree = computed(() => {
const parentForm = this.parentFieldSignalContext.form;
return parentForm[this.field().key];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "arrayFieldTree" }] : /* istanbul ignore next */ []));
currentArrayValue = computed(() => {
const tree = this.arrayFieldTree();
if (tree) {
const value = tree().value();
return Array.isArray(value) ? value : [];
}
return getArrayValue(this.parentFieldSignalContext.value(), this.field().key);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "currentArrayValue" }] : /* istanbul ignore next */ []));
arrayFieldTrees = computed(() => {
const arrayValue = this.currentArrayValue();
if (arrayValue.length === 0)
return [];
const arrayFieldTree = this.arrayFieldTree();
if (!arrayFieldTree)
return arrayValue.map(() => null);
const items = [];
for (let i = 0; i < arrayValue.length; i++) {
const itemFieldTree = arrayFieldTree[i];
items.push(itemFieldTree ?? null);
}
return items;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "arrayFieldTrees" }] : /* istanbul ignore next */ []));
resolvedItemsSignal = signal([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resolvedItemsSignal" }] : /* istanbul ignore next */ []));
fsm = new ArrayFieldStateMachine();
itemPositionMap = computed(() => {
const items = this.resolvedItemsSignal();
return new Map(items.map((item, idx) => [item.id, idx]));
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "itemPositionMap" }] : /* istanbul ignore next */ []));
resolvedItems = computed(() => this.resolvedItemsSignal(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resolvedItems" }] : /* istanbul ignore next */ []));
allResolvedFieldsRenderReady = computed(() => this.resolvedItems().every((item) => item.fields.every((field) => field.renderReady())), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "allResolvedFieldsRenderReady" }] : /* istanbul ignore next */ []));
atMaxLength = computed(() => {
const maxLength = this.field().maxLength;
if (maxLength === undefined)
return false;
return this.currentArrayValue().length >= maxLength;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "atMaxLength" }] : /* istanbul ignore next */ []));
autoRemoveCache = new WeakMap();
constructor() {
this.setupEffects();
this.setupEventHandlers();
}
setupEffects() {
explicitEffect([this.arrayFieldTrees], ([fieldTrees]) => {
this.performDifferentialUpdate(fieldTrees);
});
explicitEffect([this.fsm.state, this.allResolvedFieldsRenderReady], ([, allReady]) => {
if (this.fsm.shouldEmit(allReady)) {
emitComponentInitialized(this.eventBus, 'array', this.field().key, this.parentInjector);
}
});
}
setupEventHandlers() {
observeArrayActions(this.eventBus, () => this.key())
.pipe(takeUntilDestroyed())
.subscribe((action) => {
if (action.action === 'add') {
const templates = Array.isArray(action.template) ? action.template : action.template ? [action.template] : [];
if (templates.length === 0) {
this.logger.error(`Cannot add item to array '${this.key()}': template is REQUIRED. ` +
'Buttons must specify an explicit template property. ' +
'There is no default template - each add operation must provide its own.');
return;
}
void this.handleAddFromEvent(action.template, action.index);
}
else if (action.action === 'move') {
this.moveItem(action.fromIndex, action.toIndex);
}
else {
this.removeItem(action.index);
}
});
}
async handleAddFromEvent(template, index) {
const templates = Array.isArray(template) ? [...template] : [template];
const isSingleField = !Array.isArray(template);
const isPrimitiveItem = isSingleField && getFieldValueHandling(templates[0].type, this.rawFieldRegistry()) !== 'flatten';
if (isPrimitiveItem && templates[0].key && !this.primitiveFieldKey()) {
this.primitiveFieldKey.set(templates[0].key);
}
if (templates.length === 0) {
this.logger.error(`Cannot add item to array '${this.field().key}': no field templates provided. ` +
'Buttons must specify an explicit template property when adding array items.');
return;
}
const arrayKey = this.field().key;
const parentForm = this.parentFieldSignalContext.form;
const currentValue = parentForm().value();
const currentArray = getArrayValue(currentValue, arrayKey);
const insertIndex = index !== undefined ? Math.min(index, currentArray.length) : currentArray.length;
let value;
if (isPrimitiveItem) {
value = getFieldDefaultValue(templates[0], this.rawFieldRegistry());
}
else {
value = {};
for (const templateField of templates) {
const rawValue = getFieldDefaultValue(templateField, this.rawFieldRegistry());
const valueHandling = getFieldValueHandling(templateField.type, this.rawFieldRegistry());
const isContainer = templateField.type === 'group' || templateField.type === 'row' || templateField.type === 'container';
if (isContainer) {
if (isGroupField(templateField)) {
value = { ...value, [templateField.key]: rawValue };
}
else {
value = { ...value, ...rawValue };
}
}
else if (valueHandling === 'include' && templateField.key) {
value = { ...value, [templateField.key]: rawValue };
}
}
}
const run = this.fsm.dispatch({ kind: 'append' });
const resolveTemplates = this.withAutoRemove(templates);
const resolvedItem = await firstValueFrom(resolveArrayItem({
index: insertIndex,
templates: resolveTemplates,
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
primitiveFieldKey: isPrimitiveItem ? templates[0].key : undefined,
}).pipe(catchError((error) => {
this.logger.error(`Failed to resolve array item at index ${insertIndex}:`, error);
return of(undefined);
})));
if (run.isStale() || !resolvedItem) {
return;
}
this.slot().templates.set(resolvedItem.id, templates);
this.slot().itemOrder.splice(insertIndex, 0, resolvedItem.id);
this.resolvedItemsSignal.update((current) => {
const newItems = [...current];
newItems.splice(insertIndex, 0, resolvedItem);
return newItems;
});
const newArray = [...currentArray];
newArray.splice(insertIndex, 0, value);
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
}
performDifferentialUpdate(fieldTrees) {
const resolvedItems = this.resolvedItemsSignal();
const operation = determineDifferentialOperation(resolvedItems, fieldTrees.length);
if (operation.type === 'none') {
return;
}
switch (operation.type) {
case 'clear': {
this.slot().templates.clear();
this.resolvedItemsSignal.set([]);
const run = this.fsm.dispatch({ kind: 'clear' });
if (resolvedItems.length === 0 && !this.fsm.hasEverEmitted) {
run.resolve();
}
break;
}
case 'initial': {
const slot = this.slot();
const useSurvival = slot.itemOrder.length === fieldTrees.length && fieldTrees.length > 0;
const positionalTemplates = useSurvival ? slot.itemOrder.map((id) => slot.templates.get(id)) : undefined;
const existingItemIds = useSurvival ? slot.itemOrder.slice() : undefined;
const run = this.fsm.dispatch({ kind: 'initial' });
void this.resolveAllItems(fieldTrees, run, positionalTemplates, existingItemIds);
break;
}
case 'append': {
const run = this.fsm.dispatch({ kind: 'append' });
void this.appendItems(fieldTrees, operation.startIndex, operation.endIndex, run);
break;
}
case 'recreate': {
const positionalTemplates = resolvedItems.map((item) => {
return this.slot().templates.get(item.id);
});
this.resolvedItemsSignal.set([]);
const run = this.fsm.dispatch({ kind: 'recreate' });
void this.resolveAllItems(fieldTrees, run, positionalTemplates);
break;
}
}
}
async resolveAllItems(fieldTrees, run, positionalTemplates, existingItemIds) {
if (fieldTrees.length === 0) {
this.resolvedItemsSignal.set([]);
return;
}
const safeItemObservables = fieldTrees.map((_, i) => this.createResolveItemObservable(i, positionalTemplates?.[i], existingItemIds?.[i]).pipe(catchError((error) => {
this.logger.error(`Failed to resolve array item at index ${i}:`, error);
return of(undefined);
})));
try {
const items = await firstValueFrom(forkJoin(safeItemObservables).pipe(map((items) => items.filter((item) => item !== undefined))));
if (!run.isStale()) {
if (positionalTemplates) {
const newItemIds = new Set(items.map((item) => item.id));
for (const existingId of this.slot().templates.keys()) {
if (!newItemIds.has(existingId)) {
this.slot().templates.delete(existingId);
}
}
items.forEach((item, idx) => {
const template = positionalTemplates[idx];
if (template) {
this.slot().templates.set(item.id, template);
}
});
}
const slot = this.slot();
slot.itemOrder.length = 0;
for (const item of items)
slot.itemOrder.push(item.id);
this.resolvedItemsSignal.set(items);
run.resolve();
}
}
catch (err) {
this.logger.error('Failed to resolve array items:', err);
this.resolvedItemsSignal.set([]);
}
}
async appendItems(fieldTrees, startIndex, endIndex, run) {
const itemsToResolve = fieldTrees.slice(startIndex, endIndex);
if (itemsToResolve.length === 0)
return;
const safeItemObservables = itemsToResolve.map((_, i) => {
const index = startIndex + i;
return this.createResolveItemObservable(index).pipe(catchError((error) => {
this.logger.error(`Failed to resolve array item at index ${index}:`, error);
return of(undefined);
}));
});
try {
const newItems = await firstValueFrom(forkJoin(safeItemObservables).pipe(map((items) => items.filter((item) => item !== undefined))));
if (!run.isStale()) {
const slot = this.slot();
for (const item of newItems)
slot.itemOrder.push(item.id);
this.resolvedItemsSignal.update((current) => [...current, ...newItems]);
}
}
catch (err) {
this.logger.error('Failed to append array items:', err);
}
}
createResolveItemObservable(index, overrideTemplate, existingItemId) {
const itemTemplates = this.itemTemplates();
const primitiveKey = this.primitiveFieldKey();
if (overrideTemplate && overrideTemplate.length > 0) {
return resolveArrayItem({
index,
templates: this.withAutoRemove(overrideTemplate),
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
existingItemId,
primitiveFieldKey: primitiveKey,
});
}
const templates = itemTemplates[index];
if (templates && templates.length > 0) {
return resolveArrayItem({
index,
templates: templates,
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
existingItemId,
primitiveFieldKey: primitiveKey,
});
}
const fallback = this.fallbackTemplate();
if (fallback && fallback.length > 0) {
return resolveArrayItem({
index,
templates: this.withAutoRemove(fallback),
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
existingItemId,
primitiveFieldKey: primitiveKey,
}).pipe(tap((item) => {
if (item)
this.slot().templates.set(item.id, fallback);
}));
}
this.logger.warn(`No template found for array item at index ${index}. This likely occured for a Full-API array element that was created from the DynamicForm's value being set directly, which is currently not supported. Consider using the simplified array API.`);
return of(undefined);
}
withAutoRemove(templates) {
const removeButton = this.autoRemoveButton();
if (!removeButton)
return templates;
let cached = this.autoRemoveCache.get(templates);
if (cached)
return cached;
cached = [...templates, removeButton];
this.autoRemoveCache.set(templates, cached);
return cached;
}
moveItem(fromIndex, toIndex) {
const arrayKey = this.field().key;
const parentForm = this.parentFieldSignalContext.form;
const currentValue = parentForm().value();
const currentArray = getArrayValue(currentValue, arrayKey);
const length = currentArray.length;
if (fromIndex < 0 || fromIndex >= length) {
this.logger.warn(`moveArrayItem fromIndex ${fromIndex} is out of bounds for array '${arrayKey}' with length ${length}. Operation skipped.`);
return;
}
if (toIndex < 0 || toIndex >= length) {
this.logger.warn(`moveArrayItem toIndex ${toIndex} is out of bounds for array '${arrayKey}' with length ${length}. Operation skipped.`);
return;
}
if (fromIndex === toIndex)
return;
const rawTemplates = this.rawItemTemplates();
const currentItems = this.resolvedItemsSignal();
const lo = Math.min(fromIndex, toIndex);
const hi = Math.max(fromIndex, toIndex);
for (let i = lo; i <= hi; i++) {
const item = currentItems[i];
if (item && !this.slot().templates.has(item.id) && i < rawTemplates.length) {
this.slot().templates.set(item.id, rawTemplates[i]);
}
}
this.resolvedItemsSignal.update((current) => {
const newItems = [...current];
const [moved] = newItems.splice(fromIndex, 1);
newItems.splice(toIndex, 0, moved);
return newItems;
});
this.slot().moveItem(fromIndex, toIndex);
const newArray = [...currentArray];
const [movedValue] = newArray.splice(fromIndex, 1);
newArray.splice(toIndex, 0, movedValue);
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
}
removeItem(index) {
const arrayKey = this.field().key;
const parentForm = this.parentFieldSignalContext.form;
const currentValue = parentForm().value();
const currentArray = getArrayValue(currentValue, arrayKey);
if (currentArray.length === 0)
return;
let removeIndex;
if (index === undefined || index === -1) {
removeIndex = currentArray.length - 1;
}
else if (index < -1 || index >= currentArray.length) {
this.logger.warn(`removeArrayItem index ${index} is out of bounds for array '${arrayKey}' with length ${currentArray.length}. Operation skipped.`);
return;
}
else {
removeIndex = index;
}
const removedItem = this.resolvedItemsSignal()[removeIndex];
if (removedItem) {
this.slot().templates.delete(removedItem.id);
}
this.slot().removeAt(removeIndex);
this.resolvedItemsSignal.update((current) => {
const newItems = [...current];
newItems.splice(removeIndex, 1);
return newItems;
});
const newArray = [...currentArray];
newArray.splice(removeIndex, 1);
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: ArrayFieldComponent, isStandalone: true, selector: "array-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
@for (item of resolvedItems(); track item.id; let i = $index) {
<div
class="df-array-item"
role="group"
[attr.aria-label]="'Item ' + (i + 1)"
[attr.data-array-item-id]="item.id"
[attr.data-array-item-index]="i"
>
@for (field of item.fields; track $index) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</div>
}
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-array-item-gap, var(--df-grid-row-gap, .5rem));width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}.df-array-item{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-array-item>*{grid-column:1/-1}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'array-field', imports: [DfFieldOutlet], template: `
@for (item of resolvedItems(); track item.id; let i = $index) {
<div
class="df-array-item"
role="group"
[attr.aria-label]="'Item ' + (i + 1)"
[attr.data-array-item-id]="item.id"
[attr.data-array-item-index]="i"
>
@for (field of item.fields; track $index) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</div>
}
`, host: {
'[class]': 'hostClasses()',
'[class.df-container-hidden]': 'hidden()',
'[attr.aria-hidden]': 'hidden() || null',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-array-item-gap, var(--df-grid-row-gap, .5rem));width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}.df-array-item{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-array-item>*{grid-column:1/-1}\n"] }]
}], 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 }] }] } });
export { ArrayFieldComponent, ArrayFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-array-field.component-BVnXnaNw.mjs.map

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import * as i0 from '@angular/core';
import { inject, DestroyRef, Injector, EnvironmentInjector, viewChild, TemplateRef, ViewContainerRef, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
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-ZiNaCWHl.mjs';
import { EventBus, DynamicFormLogger, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
class ContainerFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
injector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
wrapperAutoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);
defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
childrenTpl = viewChild.required('childrenTpl', { read: TemplateRef });
wrapperContainer = viewChild.required('wrapperContainer', { read: ViewContainerRef });
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
hidden = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => computeContainerHostClasses('container', this.className()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
disabled = computed(() => this.field().disabled || false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
fieldsSource = computed(() => (this.field().fields || []), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldsSource" }] : /* istanbul ignore next */ []));
resolvedFields = derivedFromDeferred(this.fieldsSource, createFieldResolutionPipe(() => ({
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
registry: this.rawFieldRegistry(),
injector: this.injector,
destroyRef: this.destroyRef,
onError: (fieldDef, error) => {
const fieldKey = fieldDef.key || '<no key>';
const containerKey = this.field().key || '<no key>';
this.logger.error(`Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +
`within container '${containerKey}'. Ensure the field type is registered in your field registry.`, error);
},
})), { initialValue: [], injector: this.injector });
wrappers = computed(() => resolveWrappers(this.field(), this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
constructor() {
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'container', () => this.field().key, this.injector);
createWrapperChainController({
vcr: this.wrapperContainer,
wrappers: this.wrappers,
renderInnermost: (slot) => slot.createEmbeddedView(this.childrenTpl()),
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ContainerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: ContainerFieldComponent, isStandalone: true, selector: "div[container-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, viewQueries: [{ propertyName: "childrenTpl", first: true, predicate: ["childrenTpl"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "wrapperContainer", first: true, predicate: ["wrapperContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
<ng-template #childrenTpl>
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</ng-template>
<ng-container #wrapperContainer></ng-container>
`, isInline: true, styles: [":host{display:block;width:100%}:host.df-container-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ContainerFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'div[container-field]', imports: [DfFieldOutlet], template: `
<ng-template #childrenTpl>
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</ng-template>
<ng-container #wrapperContainer></ng-container>
`, host: {
'[class]': 'hostClasses()',
'[class.disabled]': 'disabled()',
'[class.df-container-hidden]': 'hidden()',
'[attr.aria-hidden]': 'hidden() || null',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host.df-container-hidden{display:none}\n"] }]
}], 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 }] }] } });
export { ContainerFieldComponent, ContainerFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-container-field.component-CM1pY9v5.mjs.map
{"version":3,"file":"ng-forge-dynamic-forms-container-field.component-CM1pY9v5.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"]}
import * as i0 from '@angular/core';
import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, linkedSignal, runInInjectionContext, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
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-ZiNaCWHl.mjs';
import { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
import { explicitEffect } from 'ngxtension/explicit-effect';
import { injectFieldSignalContext, GROUP_CONTEXT, EventBus, DynamicFormLogger, isEqual, FIELD_SIGNAL_CONTEXT, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
import { form } from '@angular/forms/signals';
class GroupFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
parentFieldSignalContext = injectFieldSignalContext();
parentGroupContext = inject(GROUP_CONTEXT, { optional: true });
injector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
hidden = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => computeContainerHostClasses('group', this.className()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
formSetup = computed(() => {
const groupField = this.field();
const registry = this.rawFieldRegistry();
if (groupField.fields && groupField.fields.length > 0) {
const flattenedFields = this.fieldProcessors.memoizedFlattenFields(groupField.fields, registry);
const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(groupField.fields, registry);
const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);
const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);
return {
fields: flattenedFieldsForRendering,
schemaFields: flattenedFields,
originalFields: groupField.fields,
defaultValues,
registry,
};
}
return {
fields: [],
schemaFields: [],
originalFields: [],
defaultValues: {},
registry,
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
defaultValues = linkedSignal(() => this.formSetup().defaultValues, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
entity = linkedSignal(() => {
const parentValue = this.parentFieldSignalContext.value();
const groupKey = this.field().key;
const defaults = this.defaultValues();
const groupValue = parentValue?.[groupKey] || {};
return { ...defaults, ...groupValue };
}, {
debugName: 'GroupFieldComponent.entity',
equal: isEqual,
});
form = computed(() => {
return runInInjectionContext(this.injector, () => {
const setup = this.formSetup();
if (setup.schemaFields.length > 0) {
const schema = createSchemaFromFields(setup.schemaFields, setup.registry);
return untracked(() => form(this.entity, schema));
}
return untracked(() => form(this.entity));
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
formValue = computed(() => this.entity(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
valid = computed(() => this.form()().valid(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
invalid = computed(() => this.form()().invalid(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
dirty = computed(() => this.form()().dirty(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
touched = computed(() => this.form()().touched(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
errors = computed(() => this.form()().errors(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
disabled = computed(() => this.form()().disabled(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
nestedFieldTree = computed(() => {
const parentForm = this.parentFieldSignalContext.form;
const groupKey = this.field().key;
return parentForm[groupKey];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "nestedFieldTree" }] : /* istanbul ignore next */ []));
groupFieldSignalContext = (() => {
const nestedFieldTree = this.nestedFieldTree;
return {
injector: this.injector,
value: this.parentFieldSignalContext.value,
defaultValues: this.defaultValues,
get form() {
return nestedFieldTree() ?? {};
},
};
})();
groupContext = {
groupPath: computed(() => {
const ownKey = this.field().key;
const parentPath = this.parentGroupContext?.groupPath();
return parentPath ? `${parentPath}.${ownKey}` : ownKey;
}),
};
groupInjector = Injector.create({
parent: this.injector,
providers: [
{ provide: FIELD_SIGNAL_CONTEXT, useValue: this.groupFieldSignalContext },
{ provide: GROUP_CONTEXT, useValue: this.groupContext },
],
});
validityChange = outputFromObservable(toObservable(this.valid));
dirtyChange = outputFromObservable(toObservable(this.dirty));
submitted = outputFromObservable(this.eventBus.on('submit'));
fieldsSource = computed(() => this.formSetup().fields, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldsSource" }] : /* istanbul ignore next */ []));
resolvedFields = derivedFromDeferred(this.fieldsSource, createFieldResolutionPipe(() => ({
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
registry: this.rawFieldRegistry(),
injector: this.groupInjector,
destroyRef: this.destroyRef,
onError: (fieldDef, error) => {
const fieldKey = fieldDef.key || '<no key>';
this.logger.error(`Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +
`within group '${this.field().key}'. Ensure the field type is registered in your field registry.`, error);
},
})), { initialValue: [], injector: this.injector });
constructor() {
this.setupEffects();
}
setupEffects() {
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'group', () => this.field().key, this.injector);
explicitEffect([this.nestedFieldTree, this.field], ([tree, field]) => {
if (!tree) {
this.logger.warn(`Group field "${field.key}" not found in parent form. ` + `Ensure the parent form schema includes this group field.`);
}
}, { defer: true });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: GroupFieldComponent, isStandalone: true, selector: "fieldset[group-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted" }, host: { attributes: { "role": "group" }, properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{border:none;margin:0;padding:0;min-width:0;display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GroupFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'fieldset[group-field]', imports: [DfFieldOutlet], template: `
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
`, host: {
'[class]': 'hostClasses()',
role: 'group',
'[class.disabled]': 'disabled()',
'[class.df-container-hidden]': 'hidden()',
'[attr.aria-hidden]': 'hidden() || null',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{border:none;margin:0;padding:0;min-width:0;display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}\n"] }]
}], 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"] }] } });
export { GroupFieldComponent, GroupFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-group-field.component-D4JhDffB.mjs.map
{"version":3,"file":"ng-forge-dynamic-forms-group-field.component-D4JhDffB.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"]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import * as i0 from '@angular/core';
import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
import { i as injectFieldRegistry, F as FIELD_WINDOWING, j as resolveFieldWindowing, c as createFieldResolutionPipe, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-ZiNaCWHl.mjs';
import { outputFromObservable } from '@angular/core/rxjs-interop';
import { explicitEffect } from 'ngxtension/explicit-effect';
import { EventBus, DynamicFormLogger, FORM_OPTIONS, validatePageNesting, derivedFromDeferred, isContainerField, getGridClassString } from '@ng-forge/dynamic-forms/internal';
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
class PageFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
injector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
formOptions = inject(FORM_OPTIONS, { optional: true });
globalFieldWindowing = inject(FIELD_WINDOWING);
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
pageIndex = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
isVisible = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => computeContainerHostClasses('page-field', this.className()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
disabled = computed(() => this.field().disabled || false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
isValid = computed(() => validatePageNesting(this.field()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isValid" }] : /* istanbul ignore next */ []));
fieldWindowing = computed(() => resolveFieldWindowing(this.globalFieldWindowing, this.formOptions?.()?.fieldWindowing), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldWindowing" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
nextPage = outputFromObservable(this.eventBus.on('next-page'));
previousPage = outputFromObservable(this.eventBus.on('previous-page'));
pageChange = outputFromObservable(this.eventBus.on('page-change'));
fieldsSource = computed(() => {
if (!this.isValid()) {
return [];
}
return this.field().fields || [];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldsSource" }] : /* istanbul ignore next */ []));
resolvedFields = derivedFromDeferred(this.fieldsSource, createFieldResolutionPipe(() => ({
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
registry: this.rawFieldRegistry(),
injector: this.injector,
destroyRef: this.destroyRef,
onError: (fieldDef, error) => {
const fieldKey = fieldDef.key || '<no key>';
this.logger.error(`Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +
`within page '${this.field().key}'. Ensure the field type is registered in your field registry.`, error);
},
})), { initialValue: [], injector: this.injector });
constructor() {
this.setupEffects();
}
windowsField(field, index) {
const w = this.fieldWindowing();
return w.enabled && index >= w.eager && !isContainerField(field.fieldDef);
}
placeholderGridClass(field) {
const grid = getGridClassString(field.fieldDef);
return grid ? `df-field-placeholder ${grid}` : 'df-field-placeholder';
}
setupEffects() {
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'page', () => this.field().key, this.injector);
explicitEffect([this.isValid, this.field], ([valid, pageField]) => {
if (!valid) {
this.logger.error(`Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +
`Pages cannot contain other pages. Consider using groups or rows for nested structure.`, pageField);
}
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PageFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: PageFieldComponent, isStandalone: true, selector: "section[page-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: true, transformFunction: null }, isVisible: { classPropertyName: "isVisible", publicName: "isVisible", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { nextPage: "nextPage", previousPage: "previousPage", pageChange: "pageChange" }, host: { properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-page-visible": "isVisible()", "class.df-page-hidden": "!isVisible()", "attr.aria-hidden": "!isVisible()", "attr.data-page-index": "pageIndex()", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
@for (field of resolvedFields(); track field.key; let i = $index) {
@if (!field.hidden()) {
@if (windowsField(field, i)) {
@defer (on viewport) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
} @placeholder {
<div
[class]="placeholderGridClass(field)"
[style.min-height]="fieldWindowing().placeholderHeight"
[attr.data-field-key]="field.key"
aria-hidden="true"
></div>
}
} @else {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
}
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;width:100%;gap:var(--df-grid-row-gap, .5rem);transition:opacity .2s ease-in-out}:host.df-page-spacing-custom{--df-grid-row-gap: var(--df-page-custom-spacing, 1.5rem)}:host.df-page-visible{opacity:1}:host.df-page-hidden{display:none;opacity:0;pointer-events:none}:host[aria-hidden=true]{pointer-events:none;visibility:hidden}@media(prefers-reduced-motion:no-preference){:host{transition:opacity var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out),transform var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out)}:host.df-page-visible{transform:translate(0)}:host.df-page-hidden{transform:translate(var(--df-page-slide-offset, -10px))}}@media(prefers-reduced-motion:reduce){:host{transition:none}}.df-field-placeholder{display:block}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [DfFieldOutlet]] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PageFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'section[page-field]', imports: [DfFieldOutlet], template: `
@for (field of resolvedFields(); track field.key; let i = $index) {
@if (!field.hidden()) {
@if (windowsField(field, i)) {
@defer (on viewport) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
} @placeholder {
<div
[class]="placeholderGridClass(field)"
[style.min-height]="fieldWindowing().placeholderHeight"
[attr.data-field-key]="field.key"
aria-hidden="true"
></div>
}
} @else {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
}
`, host: {
'[class]': 'hostClasses()',
'[class.disabled]': 'disabled()',
'[class.df-page-visible]': 'isVisible()',
'[class.df-page-hidden]': '!isVisible()',
'[attr.aria-hidden]': '!isVisible()',
'[attr.data-page-index]': 'pageIndex()',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;width:100%;gap:var(--df-grid-row-gap, .5rem);transition:opacity .2s ease-in-out}:host.df-page-spacing-custom{--df-grid-row-gap: var(--df-page-custom-spacing, 1.5rem)}:host.df-page-visible{opacity:1}:host.df-page-hidden{display:none;opacity:0;pointer-events:none}:host[aria-hidden=true]{pointer-events:none;visibility:hidden}@media(prefers-reduced-motion:no-preference){:host{transition:opacity var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out),transform var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out)}:host.df-page-visible{transform:translate(0)}:host.df-page-hidden{transform:translate(var(--df-page-slide-offset, -10px))}}@media(prefers-reduced-motion:reduce){:host{transition:none}}.df-field-placeholder{display:block}\n"] }]
}], 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"] }] } });
export { PageFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-Wbz6S8iH.mjs.map
{"version":3,"file":"ng-forge-dynamic-forms-page-field.component-Wbz6S8iH.mjs","mappings":";;;;;;;;AAwDc;YAKK;;AAEA;AACE;AACF;AACA;;AAEA;;;;;AAQjB;;;;;;YAQS;;AAEA;;AAEA;;AAET,IAKmB;;;;YAUV;AACA;AACA;YAMQ;AACf;AACE;;;;;AAKe;AAGf;AACA;;;AAGA;AACE;;;;AAOH;YAQH;;;AAIA;AAOE;AACA;;IAIQ;;;;;;AAYR;;;;;AAQA;;;AA3HiB;;;;;;;;;;;;;;;;;;;;;;;AAnCT;;;;;;;;;;;;;;;;;;;;AAwBR;AACA;AACA;AACA;AACA;AACA;AACA;AACA","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/page/page-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, EnvironmentInjector, inject, Injector, input } from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { outputFromObservable } 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 { PageField, validatePageNesting } 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 { NextPageEvent, PageChangeEvent, PreviousPageEvent } from '../../events/constants';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\nimport { FORM_OPTIONS } from '@ng-forge/dynamic-forms/internal';\nimport { getGridClassString } from '@ng-forge/dynamic-forms/internal';\nimport { isContainerField } from '@ng-forge/dynamic-forms/internal';\nimport { FIELD_WINDOWING } from '../../providers/features/field-windowing/field-windowing.token';\nimport { resolveFieldWindowing } from '../../providers/features/field-windowing/resolve-field-windowing';\n\n/** Renders a single page in multi-page (wizard) forms. */\n@Component({\n selector: 'section[page-field]',\n imports: [DfFieldOutlet],\n template: `\n @for (field of resolvedFields(); track field.key; let i = $index) {\n @if (!field.hidden()) {\n @if (windowsField(field, i)) {\n @defer (on viewport) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n } @placeholder {\n <div\n [class]=\"placeholderGridClass(field)\"\n [style.min-height]=\"fieldWindowing().placeholderHeight\"\n [attr.data-field-key]=\"field.key\"\n aria-hidden=\"true\"\n ></div>\n }\n } @else {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n }\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 protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n private readonly formOptions = inject(FORM_OPTIONS, { optional: true });\n private readonly globalFieldWindowing = inject(FIELD_WINDOWING);\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 /**\n * Effective field-windowing config for this page. Per-form\n * `FormOptions.fieldWindowing` wins over the global `withFieldWindowing()`\n * default; both fall back to disabled (fully eager).\n */\n protected readonly fieldWindowing = computed(() =>\n resolveFieldWindowing(this.globalFieldWindowing, this.formOptions?.()?.fieldWindowing),\n );\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 * Whether a field should be deferred by field windowing. Containers are never\n * windowed: they emit `ComponentInitializedEvent`, which the form's init\n * tracker counts, so deferring one would stall `initialized` until it scrolls\n * into view. Leaf fields carry no such count, so they defer freely.\n */\n protected windowsField(field: ResolvedField, index: number): boolean {\n const w = this.fieldWindowing();\n return w.enabled && index >= w.eager && !isContainerField(field.fieldDef);\n }\n\n /** Class string for a windowed field's placeholder — matches the real field's grid column. */\n protected placeholderGridClass(field: ResolvedField): string {\n const grid = getGridClassString(field.fieldDef);\n return grid ? `df-field-placeholder ${grid}` : 'df-field-placeholder';\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"]}
+6
-2

@@ -213,3 +213,5 @@ import * as i0 from '@angular/core';

indexSignal,
formValue: (arrayContext?.formValue ?? {}),
get formValue() {
return (arrayContext?.formValue ?? {});
},
isInsideArray: arrayContext !== null,

@@ -222,3 +224,5 @@ };

index: ctx.indexSignal(), arrayKey: ctx.targetArrayKey ?? '',
formValue: ctx.formValue,
get formValue() {
return ctx.formValue;
},
};

@@ -225,0 +229,0 @@ if (template) {

@@ -1,3 +0,3 @@

export { o as AppendArrayItemEvent, B as BUILT_IN_FIELDS, p as ComponentAddonComponent, q as ConsoleLogger, s as DfTemplate, u as DynamicForm, E as EventDispatcher, v as FormClearEvent, w as FormResetEvent, x as FormSubmitEvent, I as InsertArrayItemEvent, M as MoveArrayItemEvent, N as NextPageEvent, y as PageChangeEvent, z as PopArrayItemEvent, G as PrependArrayItemEvent, H as PreviousPageEvent, R as RemoveAtIndexEvent, S as ShiftArrayItemEvent, T as TemplateAddonComponent, J as TextAddonComponent, K as arrayEvent, L as createWrappers, O as isWrappersBundle, Q as provideDynamicForm, U as sanitizeFormConfig, V as withAddonActions, W as withCustomAddon, X as withEventFormValue, Y as withLegacyStatusClasses, Z as withLoggerConfig, _ as withValidationExecutionDefaults, $ as withValueExclusionDefaults, a0 as wrapperProps } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs';
export { q as AppendArrayItemEvent, B as BUILT_IN_FIELDS, s as ComponentAddonComponent, u as ConsoleLogger, v as DfTemplate, w as DynamicForm, E as EventDispatcher, x as FormClearEvent, y as FormResetEvent, z as FormSubmitEvent, I as InsertArrayItemEvent, M as MoveArrayItemEvent, N as NextPageEvent, G as PageChangeEvent, H as PopArrayItemEvent, J as PrependArrayItemEvent, K as PreviousPageEvent, R as RemoveAtIndexEvent, S as ShiftArrayItemEvent, T as TemplateAddonComponent, L as TextAddonComponent, O as arrayEvent, Q as createWrappers, U as isWrappersBundle, V as provideDynamicForm, W as sanitizeFormConfig, X as withAddonActions, Y as withCustomAddon, Z as withEventFormValue, _ as withFieldWindowing, $ as withLegacyStatusClasses, a0 as withLoggerConfig, a1 as withPagePreload, a2 as withValidationExecutionDefaults, a3 as withValueExclusionDefaults, a4 as wrapperProps } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-ZiNaCWHl.mjs';
export { DF_FIELD_TEMPLATES, DynamicFormError, DynamicFormLogger, NoopLogger, hasFormValue, isArrayField, isContainerField, isDisplayOnlyField, isFieldBoundContext, isFormStateCondition, isGroupField, isHiddenField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isWrapperTypeDefinition, resolveDynamicValue } from '@ng-forge/dynamic-forms/internal';
//# sourceMappingURL=ng-forge-dynamic-forms.mjs.map
{
"name": "@ng-forge/dynamic-forms",
"version": "1.1.0",
"version": "1.2.0-next.1",
"description": "Type-safe, signal-powered dynamic forms for Angular. Build complex forms from configuration with full TypeScript inference.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -332,3 +332,9 @@ import { FormEvent, FormEventConstructor, FormSubmitEvent, NextPageEvent, PreviousPageEvent, FieldWithValidation, ArrayItemContext, BaseAddon, DynamicValue } from '@ng-forge/dynamic-forms';

indexSignal: Signal<number>;
/** The current form value for token resolution */
/**
* Current form value for token resolution. Implemented as a lazy getter so it
* is read at event-dispatch time, not when the mapper builds its context —
* reading it eagerly inside the mapper's computed would resnapshot a stale
* value and subscribe every array button to the whole form entity per
* keystroke. Keep it a getter on any reimplementation.
*/
formValue: Record<string, unknown>;

@@ -346,3 +352,3 @@ /** Whether this button is inside an array context */

arrayKey: string;
/** The form value for token resolution */
/** Form value for token resolution — a lazy getter, resolved at dispatch time. See {@link ArrayButtonContext.formValue}. */
formValue: Record<string, unknown>;

@@ -349,0 +355,0 @@ /**

import * as rxjs from 'rxjs';
import * as _angular_core from '@angular/core';
import { Type, Injector, Signal, EnvironmentInjector, WritableSignal, Provider, EnvironmentProviders, TemplateRef } from '@angular/core';
import * as _ng_forge_dynamic_forms_internal from '@ng-forge/dynamic-forms/internal';
import { FieldDef, FormEvent, RegisteredFieldTypes, InferFormModel, FormConfig, FormOptions, EventBus, LazyComponentLoader, WrapperTypeDefinition, FieldTypeDefinition, InferFormValue as InferFormValue$1, DerivationLogLevel, Logger, ValueExclusionConfig, ValidationExecutionConfig, TextAddon, WrapperFieldInputs, TemplateAddon, ComponentAddon, BaseAddon, AddonTypeDefinition, AddonActionHandler, ArrayItemDefinition } from '@ng-forge/dynamic-forms/internal';
export { AddonActionContext, AddonActionPreset, AddonSlot, AddonTypeDefinition, AnyAddon, ArrayAllowedChildren, ArrayButtonConfig, ArrayField, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseAddon, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, CheckedFieldComponent, CommonAddonActionPreset, CommonAddonSlot, ComponentAddon, ConditionalExpression, ContainerAllowedChildren, ContainerField, ContainerFieldTypes, CssWrapper, CustomFnConfig, CustomFunction, CustomValidator, CustomValidatorConfig, DF_FIELD_TEMPLATES, DeclarativeHttpValidatorConfig, DynamicFormActionRegistry, DynamicFormAddonActionPresetRegistry, DynamicFormAddonRegistry, DynamicFormAddonSlotRegistry, DynamicFormError, DynamicFormFieldRegistry, DynamicFormLogger, DynamicText, DynamicValue, EvaluationContext, ExtractField, FieldBoundAddonActionContext, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HiddenField, HiddenScalar, HiddenValue, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpValidationResponseMapping, InferFormValue, LeafFieldTypes, Logger, LogicConfig, NarrowField, NarrowFields, NextButtonOptions, NoopLogger, OrphanAddonActionContext, PageAllowedChildren, PageField, RegisteredActionRef, RegisteredFieldTypes, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TemplateAddon, TextAddon, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationExecutionConfig, ValidationMessage, ValidationMessageResolver, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueType, WithInputSignals, WrapperConfig, WrapperTypeDefinition, hasFormValue, isArrayField, isContainerField, isDisplayOnlyField, isFieldBoundContext, isFormStateCondition, isGroupField, isHiddenField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isWrapperTypeDefinition, resolveDynamicValue } from '@ng-forge/dynamic-forms/internal';
import * as _angular_forms_signals from '@angular/forms/signals';
import { FieldTree } from '@angular/forms/signals';
import * as _angular_forms from '@angular/forms';
import * as _ng_forge_dynamic_forms_internal from '@ng-forge/dynamic-forms/internal';
import { FormEvent, FieldDef, RegisteredFieldTypes, InferFormModel, FormConfig, FormOptions, EventBus, LazyComponentLoader, WrapperTypeDefinition, FieldTypeDefinition, InferFormValue as InferFormValue$1, DerivationLogLevel, Logger, ValueExclusionConfig, ValidationExecutionConfig, TextAddon, WrapperFieldInputs, TemplateAddon, ComponentAddon, BaseAddon, AddonTypeDefinition, AddonActionHandler, ArrayItemDefinition } from '@ng-forge/dynamic-forms/internal';
export { AddonActionContext, AddonActionPreset, AddonSlot, AddonTypeDefinition, AnyAddon, ArrayAllowedChildren, ArrayButtonConfig, ArrayField, ArrayItemTemplate, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseAddon, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, CheckedFieldComponent, CommonAddonActionPreset, CommonAddonSlot, ComponentAddon, ConditionalExpression, ContainerAllowedChildren, ContainerField, ContainerFieldTypes, CssWrapper, CustomFnConfig, CustomFunction, CustomValidator, CustomValidatorConfig, DF_FIELD_TEMPLATES, DeclarativeHttpValidatorConfig, DynamicFormActionRegistry, DynamicFormAddonActionPresetRegistry, DynamicFormAddonRegistry, DynamicFormAddonSlotRegistry, DynamicFormError, DynamicFormFieldRegistry, DynamicFormLogger, DynamicText, DynamicValue, EvaluationContext, ExtractField, FieldBoundAddonActionContext, FieldComponent, FieldDef, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HiddenField, HiddenScalar, HiddenValue, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpValidationResponseMapping, InferFormValue, LeafFieldTypes, Logger, LogicConfig, NarrowField, NarrowFields, NextButtonOptions, NoopLogger, OrphanAddonActionContext, PageAllowedChildren, PageField, RegisteredActionRef, RegisteredFieldTypes, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TemplateAddon, TextAddon, TextElementType, TextField, TextProps, TokenContext, ValidationError, ValidationExecutionConfig, ValidationMessage, ValidationMessageResolver, ValidationMessages, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueType, WithInputSignals, WrapperConfig, WrapperTypeDefinition, hasFormValue, isArrayField, isContainerField, isDisplayOnlyField, isFieldBoundContext, isFormStateCondition, isGroupField, isHiddenField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isWrapperTypeDefinition, resolveDynamicValue } from '@ng-forge/dynamic-forms/internal';
import * as _angular_core from '@angular/core';
import { Type, Injector, Signal, EnvironmentInjector, WritableSignal, Provider, EnvironmentProviders, TemplateRef } from '@angular/core';
import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
/** Resolved field ready for rendering with ngComponentOutlet / DfFieldOutlet. */
interface ResolvedField {
key: string;
/** The original field definition (used by DfFieldOutlet to resolve wrappers). */
fieldDef: FieldDef<unknown>;
component: Type<unknown>;
injector: Injector;
inputs: Signal<Record<string, unknown>>;
renderReady: Signal<boolean>;
/**
* Whether the field is currently hidden. Sourced from the FieldTree's
* `state.hidden()` for value-bearing fields (set by Angular Signal Forms in
* response to `hidden(path, …)` calls), or from the mapper's explicit
* `hidden` input for non-value fields (button, text, container, …). Consumers
* must `@if (!field.hidden())` around `*dfFieldOutlet` so hidden fields are
* removed from the DOM — Angular Signal Forms warns (NG01916) otherwise.
*/
hidden: Signal<boolean>;
/**
* Global default configuration for progressive field mounting ("field
* windowing"): fields beyond `eager` render as `@defer (on viewport)`
* placeholders instead of mounting immediately, and mount only once scrolled
* near/into view.
*
* @internal
*/
interface FieldWindowingConfig {
/** Whether field windowing is active. */
readonly enabled: boolean;
/** Number of leading fields mounted eagerly (not windowed). */
readonly eager: number;
/** CSS `min-height` reserved for each unmounted field's placeholder. */
readonly placeholderHeight: string;
}

@@ -87,2 +84,22 @@

/** Resolved field ready for rendering with ngComponentOutlet / DfFieldOutlet. */
interface ResolvedField {
key: string;
/** The original field definition (used by DfFieldOutlet to resolve wrappers). */
fieldDef: FieldDef<unknown>;
component: Type<unknown>;
injector: Injector;
inputs: Signal<Record<string, unknown>>;
renderReady: Signal<boolean>;
/**
* Whether the field is currently hidden. Sourced from the FieldTree's
* `state.hidden()` for value-bearing fields (set by Angular Signal Forms in
* response to `hidden(path, …)` calls), or from the mapper's explicit
* `hidden` input for non-value fields (button, text, container, …). Consumers
* must `@if (!field.hidden())` around `*dfFieldOutlet` so hidden fields are
* removed from the DOM — Angular Signal Forms warns (NG01916) otherwise.
*/
hidden: Signal<boolean>;
}
/**

@@ -139,2 +156,3 @@ * Renders a form from a {@link FormConfig}. Attribute component on a native

private templateRegistry;
private globalFieldWindowing;
/** All `<ng-template dfTemplate="...">` instances projected into this form. */

@@ -167,2 +185,7 @@ private readonly _projectedTemplates;

nextButton?: _ng_forge_dynamic_forms.NextButtonOptions;
pagePreloadWindow?: number;
fieldWindowing?: boolean | {
eager?: number;
placeholderHeight?: string;
};
excludeValueIfHidden?: boolean;

@@ -204,2 +227,8 @@ excludeValueIfDisabled?: boolean;

/**
* Effective field-windowing config for non-paged forms. Per-form
* `FormOptions.fieldWindowing` wins over the global `withFieldWindowing()`
* default; both fall back to disabled (fully eager).
*/
protected readonly fieldWindowing: Signal<FieldWindowingConfig>;
/**
* Recursively counts container components that will emit ComponentInitializedEvent.

@@ -241,2 +270,11 @@ * Includes the dynamic-form component itself (+1).

protected onNativeSubmit(event: Event): void;
/**
* Whether a field should be deferred by field windowing. Containers are never
* windowed: they emit `ComponentInitializedEvent`, which the form's init
* tracker counts, so deferring one would stall `initialized` until it scrolls
* into view. Leaf fields carry no such count, so they defer freely.
*/
protected windowsField(field: ResolvedField, index: number): boolean;
/** Class string for a windowed field's placeholder — matches the real field's grid column. */
protected placeholderGridClass(field: ResolvedField): string;
private setupEffects;

@@ -459,2 +497,64 @@ /**

/**
* Configures the global page preload window for paged forms — how many pages on
* each side of the current page are eagerly mounted.
*
* @remarks
* Paged forms only ever display one page at a time, so mounting every page (the
* fields, containers, and change-detection of pages the user may never reach)
* is wasted work: it inflates initial-render time and makes every keystroke pay
* change detection for off-screen pages. The preload window bounds how much is
* mounted around the current page. Pages outside the window render a lightweight
* placeholder and mount the moment navigation brings them inside it.
*
* **Precedence:**
* 1. Per-form `pagePreloadWindow` on `FormOptions` — wins for that form
* 2. Global `withPagePreload(n)` — baseline default for all forms
* 3. No feature — token default (`1`, i.e. current page ±1)
*
* @param window - Pages to preload on each side of the current page. `0` mounts
* only the current page; `1` (default) mounts ±1 for flicker-free sequential
* navigation; higher values pre-warm more pages for jump navigation. Negative
* values are clamped to `0`.
* @returns A DynamicFormFeature that configures the global preload window
*
* @example
* ```typescript
* provideDynamicForm(...withMaterialFields(), withPagePreload(2));
* ```
*/
declare function withPagePreload(window: number): DynamicFormFeature<'page-preload'>;
/**
* Configures global progressive field mounting ("field windowing") for large
* flat (single-page) forms.
*
* @remarks
* A flat form mounts every field eagerly, so a large form pays the full
* initial-render cost up front and every keystroke walks all N live views —
* even for fields far off-screen. Field windowing bounds how much is mounted:
* the first `eager` fields render immediately, and the rest render as a
* lightweight placeholder that mounts the moment it scrolls near/into view.
* Validators, derivations, and values live at the Signal Forms schema level
* (not the DOM), so unmounted fields keep full form semantics.
*
* **Precedence:**
* 1. Per-form `fieldWindowing` on `FormOptions` — wins for that form
* 2. Global `withFieldWindowing(...)` — baseline default for all forms
* 3. No feature — token default (disabled, i.e. fully eager)
*
* @param config - `eager` (default `12`) and `placeholderHeight` (default `'4rem'`)
* overrides. `eager` is clamped to a non-negative integer.
* @returns A DynamicFormFeature that enables the global field windowing default
*
* @example
* ```typescript
* provideDynamicForm(...withMaterialFields(), withFieldWindowing({ eager: 20 }));
* ```
*/
declare function withFieldWindowing(config?: {
eager?: number;
placeholderHeight?: string;
}): DynamicFormFeature<'field-windowing'>;
/**
* Configures global validation execution defaults — when validators should run

@@ -887,3 +987,3 @@ * relative to a field's reactive state.

export { AppendArrayItemEvent, BUILT_IN_FIELDS, ComponentAddonComponent, ConsoleLogger, DfTemplate, DynamicForm, EventDispatcher, FormClearEvent, FormResetEvent, FormSubmitEvent, InsertArrayItemEvent, MoveArrayItemEvent, NextPageEvent, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, ShiftArrayItemEvent, TemplateAddonComponent, TextAddonComponent, arrayEvent, createWrappers, isWrappersBundle, provideDynamicForm, sanitizeFormConfig, withAddonActions, withCustomAddon, withEventFormValue, withLegacyStatusClasses, withLoggerConfig, withValidationExecutionDefaults, withValueExclusionDefaults, wrapperProps };
export { AppendArrayItemEvent, BUILT_IN_FIELDS, ComponentAddonComponent, ConsoleLogger, DfTemplate, DynamicForm, EventDispatcher, FormClearEvent, FormResetEvent, FormSubmitEvent, InsertArrayItemEvent, MoveArrayItemEvent, NextPageEvent, PageChangeEvent, PopArrayItemEvent, PrependArrayItemEvent, PreviousPageEvent, RemoveAtIndexEvent, ShiftArrayItemEvent, TemplateAddonComponent, TextAddonComponent, arrayEvent, createWrappers, isWrappersBundle, provideDynamicForm, sanitizeFormConfig, withAddonActions, withCustomAddon, withEventFormValue, withFieldWindowing, withLegacyStatusClasses, withLoggerConfig, withPagePreload, withValidationExecutionDefaults, withValueExclusionDefaults, wrapperProps };
export type { AddonActionsFeature, AddonWarning, ArrayItemContext, ArrayItemDefinitionTemplate, ExtractFieldDefs, ExtractFormValue, InferWrapperRegistry, SanitizeFormConfigOptions, SanitizedFormConfig, WrapperRegistration, WrappersBundle };
import * as i0 from '@angular/core';
import { signal, computed, runInInjectionContext, Injector, linkedSignal, inject, DestroyRef, EnvironmentInjector, input, ChangeDetectionStrategy, Component } from '@angular/core';
import { m as mapFieldToInputs, e as createHiddenSignal, f as createRenderReadySignal, i as injectFieldRegistry, A as ArrayItemRegistryService, g as getNormalizedArrayMetadata, h as getFieldDefaultValue, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { of, from, map, catchError, forkJoin, filter, firstValueFrom, tap } from 'rxjs';
import { explicitEffect } from 'ngxtension/explicit-effect';
import { isEqual, FIELD_SIGNAL_CONTEXT, ARRAY_CONTEXT, GROUP_CONTEXT, DynamicFormLogger, injectFieldSignalContext, EventBus, getFieldValueHandling, isGroupField } from '@ng-forge/dynamic-forms/internal';
import { e as emitComponentInitialized } from './ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs';
class ArrayFieldStateMachine {
_state = signal('idle', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
_runId = signal(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_runId" }] : /* istanbul ignore next */ []));
lastEmittedRunId = -1;
_hasEverEmitted = false;
state = this._state.asReadonly();
runId = this._runId.asReadonly();
get hasEverEmitted() {
return this._hasEverEmitted;
}
dispatch(op) {
const nextId = this._runId() + 1;
this._runId.set(nextId);
switch (op.kind) {
case 'clear':
case 'initial':
case 'recreate':
this._state.set('pending');
break;
case 'append':
break;
}
return {
id: nextId,
isStale: () => this._runId() !== nextId,
resolve: () => {
if (this._runId() === nextId && this._state() === 'pending') {
this._state.set('settled');
}
},
};
}
shouldEmit(allReady) {
if (this._state() !== 'settled' || !allReady)
return false;
if (this.lastEmittedRunId === this._runId())
return false;
this.lastEmittedRunId = this._runId();
this._hasEverEmitted = true;
this._state.set('idle');
return true;
}
}
function getArrayValue(value, key) {
const arrayValue = value?.[key];
return Array.isArray(arrayValue) ? arrayValue : [];
}
function determineDifferentialOperation(currentItems, newLength) {
const currentLength = currentItems.length;
if (newLength === 0) {
return { type: 'clear' };
}
if (currentLength === 0) {
return { type: 'initial', fieldTreesLength: newLength };
}
if (newLength > currentLength) {
return { type: 'append', startIndex: currentLength, endIndex: newLength };
}
if (newLength < currentLength) {
return { type: 'recreate' };
}
return { type: 'none' };
}
function createArrayItemInjectorAndInputs(options) {
const { template, indexSignal, parentFieldSignalContext, parentInjector, registry, arrayField, primitiveFieldKey } = options;
const itemFormAccessor = computed(() => {
const parentForm = parentFieldSignalContext.form;
const index = indexSignal();
const arrayFieldTree = parentForm[arrayField.key];
if (!arrayFieldTree) {
return undefined;
}
return arrayFieldTree[index];
}, { ...(ngDevMode ? { debugName: "itemFormAccessor" } : /* istanbul ignore next */ {}), equal: isEqual });
const injector = createItemInjector({
itemFormAccessor,
indexSignal,
parentFieldSignalContext,
parentInjector,
arrayField,
primitiveFieldKey,
});
const inputs = runInInjectionContext(injector, () => {
return mapFieldToInputs(template, registry);
});
return { injector, inputs };
}
function createItemInjector(options) {
const { itemFormAccessor, indexSignal, parentFieldSignalContext, parentInjector, arrayField, primitiveFieldKey } = options;
const arrayContext = {
arrayKey: arrayField.key,
index: indexSignal,
get formValue() {
return parentFieldSignalContext.value();
},
field: arrayField,
};
let cachedPrimitiveRaw;
let cachedPrimitiveWrapped;
return Injector.create({
parent: parentInjector,
providers: [
{
provide: FIELD_SIGNAL_CONTEXT,
useFactory: (injector) => ({
injector,
value: parentFieldSignalContext.value,
defaultValues: () => ({}),
get form() {
const raw = itemFormAccessor();
if (!raw) {
return {};
}
if (primitiveFieldKey) {
if (raw !== cachedPrimitiveRaw) {
cachedPrimitiveRaw = raw;
cachedPrimitiveWrapped = { [primitiveFieldKey]: raw };
}
return cachedPrimitiveWrapped;
}
return raw;
},
}),
deps: [Injector],
},
{ provide: ARRAY_CONTEXT, useValue: arrayContext },
{ provide: GROUP_CONTEXT, useValue: EMPTY_GROUP_CONTEXT },
],
});
}
const EMPTY_GROUP_CONTEXT = { groupPath: signal('') };
function resolveArrayItem(options) {
const { index, templates, arrayField, itemPositionMap, parentFieldSignalContext, parentInjector, registry, destroyRef, loadTypeComponent, generateItemId, existingItemId, primitiveFieldKey, } = options;
if (templates.length === 0) {
return of(undefined);
}
const itemId = existingItemId ?? generateItemId();
const indexSignal = linkedSignal(() => {
const positionMap = itemPositionMap();
return positionMap.get(itemId) ?? index;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
const fieldObservables = templates.map((template) => from(loadTypeComponent(template.type)).pipe(map((component) => {
if (destroyRef.destroyed) {
return undefined;
}
if (!component) {
return undefined;
}
const { injector, inputs } = createArrayItemInjectorAndInputs({
template,
indexSignal,
parentFieldSignalContext,
parentInjector,
registry,
arrayField,
primitiveFieldKey,
});
if (!inputs) {
return undefined;
}
return {
key: template.key,
fieldDef: template,
component,
injector,
inputs,
renderReady: createRenderReadySignal(inputs, registry.get(template.type)),
hidden: createHiddenSignal(inputs),
};
}), catchError((error) => {
if (!destroyRef.destroyed) {
const logger = parentInjector.get(DynamicFormLogger);
logger.error(`Failed to load component for field type '${template.type}' at index ${index} ` +
`within array '${arrayField.key}'. Ensure the field type is registered in your field registry.`, error);
}
return of(undefined);
})));
return forkJoin(fieldObservables).pipe(map((fields) => {
const validFields = fields.filter((f) => f !== undefined);
if (validFields.length === 0) {
return undefined;
}
return {
id: itemId,
fields: validFields,
};
}));
}
const ARRAY_EVENT_TYPES = [
'append-array-item',
'prepend-array-item',
'insert-array-item',
'move-array-item',
'pop-array-item',
'shift-array-item',
'remove-at-index',
];
function toArrayAction(event) {
switch (event.type) {
case 'append-array-item':
return { action: 'add', template: event.template };
case 'prepend-array-item':
return { action: 'add', template: event.template, index: 0 };
case 'insert-array-item':
return { action: 'add', template: event.template, index: event.index };
case 'move-array-item':
return { action: 'move', fromIndex: event.fromIndex, toIndex: event.toIndex };
case 'pop-array-item':
return { action: 'remove' };
case 'shift-array-item':
return { action: 'remove', index: 0 };
case 'remove-at-index':
return { action: 'remove', index: event.index };
}
}
function observeArrayActions(eventBus, arrayKey) {
return eventBus.on(ARRAY_EVENT_TYPES).pipe(filter((event) => event.arrayKey === arrayKey()), map(toArrayAction));
}
class ArrayFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
parentFieldSignalContext = injectFieldSignalContext();
parentInjector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
arrayItemRegistry = inject(ArrayItemRegistryService);
slot = computed(() => this.arrayItemRegistry.slotFor(this.field().key), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "slot" }] : /* istanbul ignore next */ []));
generateItemId = () => this.slot().nextId();
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
hidden = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => {
const base = 'df-field df-array';
const custom = this.className();
return custom ? `${base} ${custom}` : base;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
autoRemoveButton = computed(() => {
const metadata = getNormalizedArrayMetadata(this.field());
return metadata?.autoRemoveButton;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "autoRemoveButton" }] : /* istanbul ignore next */ []));
primitiveFieldKey = linkedSignal(() => {
const metadata = getNormalizedArrayMetadata(this.field());
if (metadata?.primitiveFieldKey) {
return metadata.primitiveFieldKey;
}
const definitions = this.field().fields || [];
if (definitions.length > 0 && !Array.isArray(definitions[0])) {
return definitions[0].key;
}
return undefined;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
rawItemTemplates = computed(() => {
const arrayField = this.field();
const definitions = arrayField.fields || [];
return definitions.map((def) => {
return (Array.isArray(def) ? def : [def]);
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawItemTemplates" }] : /* istanbul ignore next */ []));
fallbackTemplate = computed(() => {
const raw = getNormalizedArrayMetadata(this.field())?.template;
if (!raw)
return undefined;
return (Array.isArray(raw) ? [...raw] : [raw]);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fallbackTemplate" }] : /* istanbul ignore next */ []));
itemTemplates = computed(() => {
const raw = this.rawItemTemplates();
const removeButton = this.autoRemoveButton();
if (!removeButton)
return raw;
return raw.map((normalized) => {
return [...normalized, removeButton];
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "itemTemplates" }] : /* istanbul ignore next */ []));
arrayFieldTree = computed(() => {
const parentForm = this.parentFieldSignalContext.form;
return parentForm[this.field().key];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "arrayFieldTree" }] : /* istanbul ignore next */ []));
currentArrayValue = computed(() => {
const tree = this.arrayFieldTree();
if (tree) {
const value = tree().value();
return Array.isArray(value) ? value : [];
}
return getArrayValue(this.parentFieldSignalContext.value(), this.field().key);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "currentArrayValue" }] : /* istanbul ignore next */ []));
arrayFieldTrees = computed(() => {
const arrayValue = this.currentArrayValue();
if (arrayValue.length === 0)
return [];
const arrayFieldTree = this.arrayFieldTree();
if (!arrayFieldTree)
return arrayValue.map(() => null);
const items = [];
for (let i = 0; i < arrayValue.length; i++) {
const itemFieldTree = arrayFieldTree[i];
items.push(itemFieldTree ?? null);
}
return items;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "arrayFieldTrees" }] : /* istanbul ignore next */ []));
resolvedItemsSignal = signal([], /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resolvedItemsSignal" }] : /* istanbul ignore next */ []));
fsm = new ArrayFieldStateMachine();
itemPositionMap = computed(() => {
const items = this.resolvedItemsSignal();
return new Map(items.map((item, idx) => [item.id, idx]));
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "itemPositionMap" }] : /* istanbul ignore next */ []));
resolvedItems = computed(() => this.resolvedItemsSignal(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "resolvedItems" }] : /* istanbul ignore next */ []));
allResolvedFieldsRenderReady = computed(() => this.resolvedItems().every((item) => item.fields.every((field) => field.renderReady())), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "allResolvedFieldsRenderReady" }] : /* istanbul ignore next */ []));
atMaxLength = computed(() => {
const maxLength = this.field().maxLength;
if (maxLength === undefined)
return false;
return this.currentArrayValue().length >= maxLength;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "atMaxLength" }] : /* istanbul ignore next */ []));
autoRemoveCache = new WeakMap();
constructor() {
this.setupEffects();
this.setupEventHandlers();
}
setupEffects() {
explicitEffect([this.arrayFieldTrees], ([fieldTrees]) => {
this.performDifferentialUpdate(fieldTrees);
});
explicitEffect([this.fsm.state, this.allResolvedFieldsRenderReady], ([, allReady]) => {
if (this.fsm.shouldEmit(allReady)) {
emitComponentInitialized(this.eventBus, 'array', this.field().key, this.parentInjector);
}
});
}
setupEventHandlers() {
observeArrayActions(this.eventBus, () => this.key())
.pipe(takeUntilDestroyed())
.subscribe((action) => {
if (action.action === 'add') {
const templates = Array.isArray(action.template) ? action.template : action.template ? [action.template] : [];
if (templates.length === 0) {
this.logger.error(`Cannot add item to array '${this.key()}': template is REQUIRED. ` +
'Buttons must specify an explicit template property. ' +
'There is no default template - each add operation must provide its own.');
return;
}
void this.handleAddFromEvent(action.template, action.index);
}
else if (action.action === 'move') {
this.moveItem(action.fromIndex, action.toIndex);
}
else {
this.removeItem(action.index);
}
});
}
async handleAddFromEvent(template, index) {
const templates = Array.isArray(template) ? [...template] : [template];
const isSingleField = !Array.isArray(template);
const isPrimitiveItem = isSingleField && getFieldValueHandling(templates[0].type, this.rawFieldRegistry()) !== 'flatten';
if (isPrimitiveItem && templates[0].key && !this.primitiveFieldKey()) {
this.primitiveFieldKey.set(templates[0].key);
}
if (templates.length === 0) {
this.logger.error(`Cannot add item to array '${this.field().key}': no field templates provided. ` +
'Buttons must specify an explicit template property when adding array items.');
return;
}
const arrayKey = this.field().key;
const parentForm = this.parentFieldSignalContext.form;
const currentValue = parentForm().value();
const currentArray = getArrayValue(currentValue, arrayKey);
const insertIndex = index !== undefined ? Math.min(index, currentArray.length) : currentArray.length;
let value;
if (isPrimitiveItem) {
value = getFieldDefaultValue(templates[0], this.rawFieldRegistry());
}
else {
value = {};
for (const templateField of templates) {
const rawValue = getFieldDefaultValue(templateField, this.rawFieldRegistry());
const valueHandling = getFieldValueHandling(templateField.type, this.rawFieldRegistry());
const isContainer = templateField.type === 'group' || templateField.type === 'row' || templateField.type === 'container';
if (isContainer) {
if (isGroupField(templateField)) {
value = { ...value, [templateField.key]: rawValue };
}
else {
value = { ...value, ...rawValue };
}
}
else if (valueHandling === 'include' && templateField.key) {
value = { ...value, [templateField.key]: rawValue };
}
}
}
const run = this.fsm.dispatch({ kind: 'append' });
const resolveTemplates = this.withAutoRemove(templates);
const resolvedItem = await firstValueFrom(resolveArrayItem({
index: insertIndex,
templates: resolveTemplates,
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
primitiveFieldKey: isPrimitiveItem ? templates[0].key : undefined,
}).pipe(catchError((error) => {
this.logger.error(`Failed to resolve array item at index ${insertIndex}:`, error);
return of(undefined);
})));
if (run.isStale() || !resolvedItem) {
return;
}
this.slot().templates.set(resolvedItem.id, templates);
this.slot().itemOrder.splice(insertIndex, 0, resolvedItem.id);
this.resolvedItemsSignal.update((current) => {
const newItems = [...current];
newItems.splice(insertIndex, 0, resolvedItem);
return newItems;
});
const newArray = [...currentArray];
newArray.splice(insertIndex, 0, value);
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
}
performDifferentialUpdate(fieldTrees) {
const resolvedItems = this.resolvedItemsSignal();
const operation = determineDifferentialOperation(resolvedItems, fieldTrees.length);
if (operation.type === 'none') {
return;
}
switch (operation.type) {
case 'clear': {
this.slot().templates.clear();
this.resolvedItemsSignal.set([]);
const run = this.fsm.dispatch({ kind: 'clear' });
if (resolvedItems.length === 0 && !this.fsm.hasEverEmitted) {
run.resolve();
}
break;
}
case 'initial': {
const slot = this.slot();
const useSurvival = slot.itemOrder.length === fieldTrees.length && fieldTrees.length > 0;
const positionalTemplates = useSurvival ? slot.itemOrder.map((id) => slot.templates.get(id)) : undefined;
const existingItemIds = useSurvival ? slot.itemOrder.slice() : undefined;
const run = this.fsm.dispatch({ kind: 'initial' });
void this.resolveAllItems(fieldTrees, run, positionalTemplates, existingItemIds);
break;
}
case 'append': {
const run = this.fsm.dispatch({ kind: 'append' });
void this.appendItems(fieldTrees, operation.startIndex, operation.endIndex, run);
break;
}
case 'recreate': {
const positionalTemplates = resolvedItems.map((item) => {
return this.slot().templates.get(item.id);
});
this.resolvedItemsSignal.set([]);
const run = this.fsm.dispatch({ kind: 'recreate' });
void this.resolveAllItems(fieldTrees, run, positionalTemplates);
break;
}
}
}
async resolveAllItems(fieldTrees, run, positionalTemplates, existingItemIds) {
if (fieldTrees.length === 0) {
this.resolvedItemsSignal.set([]);
return;
}
const safeItemObservables = fieldTrees.map((_, i) => this.createResolveItemObservable(i, positionalTemplates?.[i], existingItemIds?.[i]).pipe(catchError((error) => {
this.logger.error(`Failed to resolve array item at index ${i}:`, error);
return of(undefined);
})));
try {
const items = await firstValueFrom(forkJoin(safeItemObservables).pipe(map((items) => items.filter((item) => item !== undefined))));
if (!run.isStale()) {
if (positionalTemplates) {
const newItemIds = new Set(items.map((item) => item.id));
for (const existingId of this.slot().templates.keys()) {
if (!newItemIds.has(existingId)) {
this.slot().templates.delete(existingId);
}
}
items.forEach((item, idx) => {
const template = positionalTemplates[idx];
if (template) {
this.slot().templates.set(item.id, template);
}
});
}
const slot = this.slot();
slot.itemOrder.length = 0;
for (const item of items)
slot.itemOrder.push(item.id);
this.resolvedItemsSignal.set(items);
run.resolve();
}
}
catch (err) {
this.logger.error('Failed to resolve array items:', err);
this.resolvedItemsSignal.set([]);
}
}
async appendItems(fieldTrees, startIndex, endIndex, run) {
const itemsToResolve = fieldTrees.slice(startIndex, endIndex);
if (itemsToResolve.length === 0)
return;
const safeItemObservables = itemsToResolve.map((_, i) => {
const index = startIndex + i;
return this.createResolveItemObservable(index).pipe(catchError((error) => {
this.logger.error(`Failed to resolve array item at index ${index}:`, error);
return of(undefined);
}));
});
try {
const newItems = await firstValueFrom(forkJoin(safeItemObservables).pipe(map((items) => items.filter((item) => item !== undefined))));
if (!run.isStale()) {
const slot = this.slot();
for (const item of newItems)
slot.itemOrder.push(item.id);
this.resolvedItemsSignal.update((current) => [...current, ...newItems]);
}
}
catch (err) {
this.logger.error('Failed to append array items:', err);
}
}
createResolveItemObservable(index, overrideTemplate, existingItemId) {
const itemTemplates = this.itemTemplates();
const primitiveKey = this.primitiveFieldKey();
if (overrideTemplate && overrideTemplate.length > 0) {
return resolveArrayItem({
index,
templates: this.withAutoRemove(overrideTemplate),
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
existingItemId,
primitiveFieldKey: primitiveKey,
});
}
const templates = itemTemplates[index];
if (templates && templates.length > 0) {
return resolveArrayItem({
index,
templates: templates,
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
existingItemId,
primitiveFieldKey: primitiveKey,
});
}
const fallback = this.fallbackTemplate();
if (fallback && fallback.length > 0) {
return resolveArrayItem({
index,
templates: this.withAutoRemove(fallback),
arrayField: this.field(),
itemPositionMap: this.itemPositionMap,
parentFieldSignalContext: this.parentFieldSignalContext,
parentInjector: this.parentInjector,
registry: this.rawFieldRegistry(),
destroyRef: this.destroyRef,
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
generateItemId: this.generateItemId,
existingItemId,
primitiveFieldKey: primitiveKey,
}).pipe(tap((item) => {
if (item)
this.slot().templates.set(item.id, fallback);
}));
}
this.logger.warn(`No template found for array item at index ${index}. This likely occured for a Full-API array element that was created from the DynamicForm's value being set directly, which is currently not supported. Consider using the simplified array API.`);
return of(undefined);
}
withAutoRemove(templates) {
const removeButton = this.autoRemoveButton();
if (!removeButton)
return templates;
let cached = this.autoRemoveCache.get(templates);
if (cached)
return cached;
cached = [...templates, removeButton];
this.autoRemoveCache.set(templates, cached);
return cached;
}
moveItem(fromIndex, toIndex) {
const arrayKey = this.field().key;
const parentForm = this.parentFieldSignalContext.form;
const currentValue = parentForm().value();
const currentArray = getArrayValue(currentValue, arrayKey);
const length = currentArray.length;
if (fromIndex < 0 || fromIndex >= length) {
this.logger.warn(`moveArrayItem fromIndex ${fromIndex} is out of bounds for array '${arrayKey}' with length ${length}. Operation skipped.`);
return;
}
if (toIndex < 0 || toIndex >= length) {
this.logger.warn(`moveArrayItem toIndex ${toIndex} is out of bounds for array '${arrayKey}' with length ${length}. Operation skipped.`);
return;
}
if (fromIndex === toIndex)
return;
const rawTemplates = this.rawItemTemplates();
const currentItems = this.resolvedItemsSignal();
const lo = Math.min(fromIndex, toIndex);
const hi = Math.max(fromIndex, toIndex);
for (let i = lo; i <= hi; i++) {
const item = currentItems[i];
if (item && !this.slot().templates.has(item.id) && i < rawTemplates.length) {
this.slot().templates.set(item.id, rawTemplates[i]);
}
}
this.resolvedItemsSignal.update((current) => {
const newItems = [...current];
const [moved] = newItems.splice(fromIndex, 1);
newItems.splice(toIndex, 0, moved);
return newItems;
});
this.slot().moveItem(fromIndex, toIndex);
const newArray = [...currentArray];
const [movedValue] = newArray.splice(fromIndex, 1);
newArray.splice(toIndex, 0, movedValue);
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
}
removeItem(index) {
const arrayKey = this.field().key;
const parentForm = this.parentFieldSignalContext.form;
const currentValue = parentForm().value();
const currentArray = getArrayValue(currentValue, arrayKey);
if (currentArray.length === 0)
return;
let removeIndex;
if (index === undefined || index === -1) {
removeIndex = currentArray.length - 1;
}
else if (index < -1 || index >= currentArray.length) {
this.logger.warn(`removeArrayItem index ${index} is out of bounds for array '${arrayKey}' with length ${currentArray.length}. Operation skipped.`);
return;
}
else {
removeIndex = index;
}
const removedItem = this.resolvedItemsSignal()[removeIndex];
if (removedItem) {
this.slot().templates.delete(removedItem.id);
}
this.slot().removeAt(removeIndex);
this.resolvedItemsSignal.update((current) => {
const newItems = [...current];
newItems.splice(removeIndex, 1);
return newItems;
});
const newArray = [...currentArray];
newArray.splice(removeIndex, 1);
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: ArrayFieldComponent, isStandalone: true, selector: "array-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
@for (item of resolvedItems(); track item.id; let i = $index) {
<div
class="df-array-item"
role="group"
[attr.aria-label]="'Item ' + (i + 1)"
[attr.data-array-item-id]="item.id"
[attr.data-array-item-index]="i"
>
@for (field of item.fields; track $index) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</div>
}
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-array-item-gap, var(--df-grid-row-gap, .5rem));width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}.df-array-item{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-array-item>*{grid-column:1/-1}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'array-field', imports: [DfFieldOutlet], template: `
@for (item of resolvedItems(); track item.id; let i = $index) {
<div
class="df-array-item"
role="group"
[attr.aria-label]="'Item ' + (i + 1)"
[attr.data-array-item-id]="item.id"
[attr.data-array-item-index]="i"
>
@for (field of item.fields; track $index) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</div>
}
`, host: {
'[class]': 'hostClasses()',
'[class.df-container-hidden]': 'hidden()',
'[attr.aria-hidden]': 'hidden() || null',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-array-item-gap, var(--df-grid-row-gap, .5rem));width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}.df-array-item{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-array-item>*{grid-column:1/-1}\n"] }]
}], 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 }] }] } });
export { ArrayFieldComponent, ArrayFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-array-field.component-CvjDqYyq.mjs.map

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import * as i0 from '@angular/core';
import { inject, DestroyRef, Injector, EnvironmentInjector, viewChild, TemplateRef, ViewContainerRef, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
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-Du3V5AOb.mjs';
import { EventBus, DynamicFormLogger, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
class ContainerFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
injector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
wrapperAutoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);
defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
childrenTpl = viewChild.required('childrenTpl', { read: TemplateRef });
wrapperContainer = viewChild.required('wrapperContainer', { read: ViewContainerRef });
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
hidden = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => computeContainerHostClasses('container', this.className()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
disabled = computed(() => this.field().disabled || false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
fieldsSource = computed(() => (this.field().fields || []), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldsSource" }] : /* istanbul ignore next */ []));
resolvedFields = derivedFromDeferred(this.fieldsSource, createFieldResolutionPipe(() => ({
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
registry: this.rawFieldRegistry(),
injector: this.injector,
destroyRef: this.destroyRef,
onError: (fieldDef, error) => {
const fieldKey = fieldDef.key || '<no key>';
const containerKey = this.field().key || '<no key>';
this.logger.error(`Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +
`within container '${containerKey}'. Ensure the field type is registered in your field registry.`, error);
},
})), { initialValue: [], injector: this.injector });
wrappers = computed(() => resolveWrappers(this.field(), this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
constructor() {
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'container', () => this.field().key, this.injector);
createWrapperChainController({
vcr: this.wrapperContainer,
wrappers: this.wrappers,
renderInnermost: (slot) => slot.createEmbeddedView(this.childrenTpl()),
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ContainerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: ContainerFieldComponent, isStandalone: true, selector: "div[container-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, viewQueries: [{ propertyName: "childrenTpl", first: true, predicate: ["childrenTpl"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "wrapperContainer", first: true, predicate: ["wrapperContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
<ng-template #childrenTpl>
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</ng-template>
<ng-container #wrapperContainer></ng-container>
`, isInline: true, styles: [":host{display:block;width:100%}:host.df-container-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ContainerFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'div[container-field]', imports: [DfFieldOutlet], template: `
<ng-template #childrenTpl>
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
</ng-template>
<ng-container #wrapperContainer></ng-container>
`, host: {
'[class]': 'hostClasses()',
'[class.disabled]': 'disabled()',
'[class.df-container-hidden]': 'hidden()',
'[attr.aria-hidden]': 'hidden() || null',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host.df-container-hidden{display:none}\n"] }]
}], 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 }] }] } });
export { ContainerFieldComponent, ContainerFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-container-field.component-DZfVWjiM.mjs.map
{"version":3,"file":"ng-forge-dynamic-forms-container-field.component-DZfVWjiM.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"]}
import * as i0 from '@angular/core';
import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, linkedSignal, runInInjectionContext, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
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-Du3V5AOb.mjs';
import { outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
import { explicitEffect } from 'ngxtension/explicit-effect';
import { injectFieldSignalContext, GROUP_CONTEXT, EventBus, DynamicFormLogger, isEqual, FIELD_SIGNAL_CONTEXT, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
import { form } from '@angular/forms/signals';
class GroupFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
parentFieldSignalContext = injectFieldSignalContext();
parentGroupContext = inject(GROUP_CONTEXT, { optional: true });
injector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
hidden = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => computeContainerHostClasses('group', this.className()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
formSetup = computed(() => {
const groupField = this.field();
const registry = this.rawFieldRegistry();
if (groupField.fields && groupField.fields.length > 0) {
const flattenedFields = this.fieldProcessors.memoizedFlattenFields(groupField.fields, registry);
const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(groupField.fields, registry);
const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);
const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);
return {
fields: flattenedFieldsForRendering,
schemaFields: flattenedFields,
originalFields: groupField.fields,
defaultValues,
registry,
};
}
return {
fields: [],
schemaFields: [],
originalFields: [],
defaultValues: {},
registry,
};
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
defaultValues = linkedSignal(() => this.formSetup().defaultValues, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
entity = linkedSignal(() => {
const parentValue = this.parentFieldSignalContext.value();
const groupKey = this.field().key;
const defaults = this.defaultValues();
const groupValue = parentValue?.[groupKey] || {};
return { ...defaults, ...groupValue };
}, {
debugName: 'GroupFieldComponent.entity',
equal: isEqual,
});
form = computed(() => {
return runInInjectionContext(this.injector, () => {
const setup = this.formSetup();
if (setup.schemaFields.length > 0) {
const schema = createSchemaFromFields(setup.schemaFields, setup.registry);
return untracked(() => form(this.entity, schema));
}
return untracked(() => form(this.entity));
});
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
formValue = computed(() => this.entity(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
valid = computed(() => this.form()().valid(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
invalid = computed(() => this.form()().invalid(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
dirty = computed(() => this.form()().dirty(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
touched = computed(() => this.form()().touched(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
errors = computed(() => this.form()().errors(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
disabled = computed(() => this.form()().disabled(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
nestedFieldTree = computed(() => {
const parentForm = this.parentFieldSignalContext.form;
const groupKey = this.field().key;
return parentForm[groupKey];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "nestedFieldTree" }] : /* istanbul ignore next */ []));
groupFieldSignalContext = (() => {
const nestedFieldTree = this.nestedFieldTree;
return {
injector: this.injector,
value: this.parentFieldSignalContext.value,
defaultValues: this.defaultValues,
get form() {
return nestedFieldTree() ?? {};
},
};
})();
groupContext = {
groupPath: computed(() => {
const ownKey = this.field().key;
const parentPath = this.parentGroupContext?.groupPath();
return parentPath ? `${parentPath}.${ownKey}` : ownKey;
}),
};
groupInjector = Injector.create({
parent: this.injector,
providers: [
{ provide: FIELD_SIGNAL_CONTEXT, useValue: this.groupFieldSignalContext },
{ provide: GROUP_CONTEXT, useValue: this.groupContext },
],
});
validityChange = outputFromObservable(toObservable(this.valid));
dirtyChange = outputFromObservable(toObservable(this.dirty));
submitted = outputFromObservable(this.eventBus.on('submit'));
fieldsSource = computed(() => this.formSetup().fields, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldsSource" }] : /* istanbul ignore next */ []));
resolvedFields = derivedFromDeferred(this.fieldsSource, createFieldResolutionPipe(() => ({
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
registry: this.rawFieldRegistry(),
injector: this.groupInjector,
destroyRef: this.destroyRef,
onError: (fieldDef, error) => {
const fieldKey = fieldDef.key || '<no key>';
this.logger.error(`Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +
`within group '${this.field().key}'. Ensure the field type is registered in your field registry.`, error);
},
})), { initialValue: [], injector: this.injector });
constructor() {
this.setupEffects();
}
setupEffects() {
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'group', () => this.field().key, this.injector);
explicitEffect([this.nestedFieldTree, this.field], ([tree, field]) => {
if (!tree) {
this.logger.warn(`Group field "${field.key}" not found in parent form. ` + `Ensure the parent form schema includes this group field.`);
}
}, { defer: true });
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: GroupFieldComponent, isStandalone: true, selector: "fieldset[group-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted" }, host: { attributes: { "role": "group" }, properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-container-hidden": "hidden()", "attr.aria-hidden": "hidden() || null", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{border:none;margin:0;padding:0;min-width:0;display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GroupFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'fieldset[group-field]', imports: [DfFieldOutlet], template: `
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
`, host: {
'[class]': 'hostClasses()',
role: 'group',
'[class.disabled]': 'disabled()',
'[class.df-container-hidden]': 'hidden()',
'[attr.aria-hidden]': 'hidden() || null',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{border:none;margin:0;padding:0;min-width:0;display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-container-hidden{display:none}\n"] }]
}], 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"] }] } });
export { GroupFieldComponent, GroupFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-group-field.component-CPFgu3pM.mjs.map
{"version":3,"file":"ng-forge-dynamic-forms-group-field.component-CPFgu3pM.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"]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import * as i0 from '@angular/core';
import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
import { i as injectFieldRegistry, c as createFieldResolutionPipe, D as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs';
import { outputFromObservable } from '@angular/core/rxjs-interop';
import { explicitEffect } from 'ngxtension/explicit-effect';
import { EventBus, DynamicFormLogger, validatePageNesting, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs';
class PageFieldComponent {
destroyRef = inject(DestroyRef);
fieldRegistry = injectFieldRegistry();
injector = inject(Injector);
environmentInjector = inject(EnvironmentInjector);
eventBus = inject(EventBus);
logger = inject(DynamicFormLogger);
field = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
key = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
className = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "className" }] : /* istanbul ignore next */ []));
pageIndex = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
isVisible = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
hostClasses = computed(() => computeContainerHostClasses('page-field', this.className()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
disabled = computed(() => this.field().disabled || false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
isValid = computed(() => validatePageNesting(this.field()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isValid" }] : /* istanbul ignore next */ []));
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
nextPage = outputFromObservable(this.eventBus.on('next-page'));
previousPage = outputFromObservable(this.eventBus.on('previous-page'));
pageChange = outputFromObservable(this.eventBus.on('page-change'));
fieldsSource = computed(() => {
if (!this.isValid()) {
return [];
}
return this.field().fields || [];
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fieldsSource" }] : /* istanbul ignore next */ []));
resolvedFields = derivedFromDeferred(this.fieldsSource, createFieldResolutionPipe(() => ({
loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
registry: this.rawFieldRegistry(),
injector: this.injector,
destroyRef: this.destroyRef,
onError: (fieldDef, error) => {
const fieldKey = fieldDef.key || '<no key>';
this.logger.error(`Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +
`within page '${this.field().key}'. Ensure the field type is registered in your field registry.`, error);
},
})), { initialValue: [], injector: this.injector });
constructor() {
this.setupEffects();
}
setupEffects() {
setupContainerInitEffect(this.resolvedFields, this.eventBus, 'page', () => this.field().key, this.injector);
explicitEffect([this.isValid, this.field], ([valid, pageField]) => {
if (!valid) {
this.logger.error(`Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +
`Pages cannot contain other pages. Consider using groups or rows for nested structure.`, pageField);
}
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PageFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: PageFieldComponent, isStandalone: true, selector: "section[page-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: true, transformFunction: null }, isVisible: { classPropertyName: "isVisible", publicName: "isVisible", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { nextPage: "nextPage", previousPage: "previousPage", pageChange: "pageChange" }, host: { properties: { "class": "hostClasses()", "class.disabled": "disabled()", "class.df-page-visible": "isVisible()", "class.df-page-hidden": "!isVisible()", "attr.aria-hidden": "!isVisible()", "attr.data-page-index": "pageIndex()", "id": "`${key()}`", "attr.data-testid": "key()" } }, ngImport: i0, template: `
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
`, isInline: true, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;width:100%;gap:var(--df-grid-row-gap, .5rem);transition:opacity .2s ease-in-out}:host.df-page-spacing-custom{--df-grid-row-gap: var(--df-page-custom-spacing, 1.5rem)}:host.df-page-visible{opacity:1}:host.df-page-hidden{display:none;opacity:0;pointer-events:none}:host[aria-hidden=true]{pointer-events:none;visibility:hidden}@media(prefers-reduced-motion:no-preference){:host{transition:opacity var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out),transform var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out)}:host.df-page-visible{transform:translate(0)}:host.df-page-hidden{transform:translate(var(--df-page-slide-offset, -10px))}}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PageFieldComponent, decorators: [{
type: Component,
args: [{ selector: 'section[page-field]', imports: [DfFieldOutlet], template: `
@for (field of resolvedFields(); track field.key) {
@if (!field.hidden()) {
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
}
}
`, host: {
'[class]': 'hostClasses()',
'[class.disabled]': 'disabled()',
'[class.df-page-visible]': 'isVisible()',
'[class.df-page-hidden]': '!isVisible()',
'[attr.aria-hidden]': '!isVisible()',
'[attr.data-page-index]': 'pageIndex()',
'[id]': '`${key()}`',
'[attr.data-testid]': 'key()',
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap, .5rem);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-sm, .5rem)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns, 12),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-md, .5rem)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-lg, .5rem)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl, .5rem);--df-grid-row-gap: var(--df-grid-row-gap-xl, .5rem)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;width:100%;gap:var(--df-grid-row-gap, .5rem);transition:opacity .2s ease-in-out}:host.df-page-spacing-custom{--df-grid-row-gap: var(--df-page-custom-spacing, 1.5rem)}:host.df-page-visible{opacity:1}:host.df-page-hidden{display:none;opacity:0;pointer-events:none}:host[aria-hidden=true]{pointer-events:none;visibility:hidden}@media(prefers-reduced-motion:no-preference){:host{transition:opacity var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out),transform var(--df-page-transition-duration, .3s) var(--df-page-transition-timing, ease-in-out)}:host.df-page-visible{transform:translate(0)}:host.df-page-hidden{transform:translate(var(--df-page-slide-offset, -10px))}}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
}], 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"] }] } });
export { PageFieldComponent as default };
//# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs.map
{"version":3,"file":"ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs","mappings":";;;;;;;;AAsCc;YAKK;;AAEA;AACE;AACF;AACA;;;;;AAQjB;;;;;;YAQS;;AAEA;;AAEA;;;;YAQA;AACA;AACA;YAMQ;AACf;AACE;;;;;AAKe;AAGf;AACA;;;AAGA;AACE;;;;AAOH;YAQH;;;;;AAWE;;;;;AAQA;;;AA/FiB;;;;;;AAdlB;;;;AARS;;;;;;;AAWR;AACA;AACA;AACA;AACA;AACA;AACA;AACA","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/page/page-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, EnvironmentInjector, inject, Injector, input } from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { outputFromObservable } 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 { PageField, validatePageNesting } 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 { NextPageEvent, PageChangeEvent, PreviousPageEvent } from '../../events/constants';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\n\n/** Renders a single page in multi-page (wizard) forms. */\n@Component({\n selector: 'section[page-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: './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 protected readonly environmentInjector = inject(EnvironmentInjector);\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"]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display