@ng-forge/dynamic-forms 0.9.0-next.8 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +1 -0
  2. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-CltYGHg9.mjs → ng-forge-dynamic-forms-array-field.component-Tw49K5kz.mjs} +171 -73
  3. package/fesm2022/ng-forge-dynamic-forms-array-field.component-Tw49K5kz.mjs.map +1 -0
  4. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-D4Fpr2ri.mjs → ng-forge-dynamic-forms-container-field.component-CrOIZEL_.mjs} +12 -8
  5. package/fesm2022/ng-forge-dynamic-forms-container-field.component-CrOIZEL_.mjs.map +1 -0
  6. package/fesm2022/{ng-forge-dynamic-forms-container-utils-BTiqZ8Np.mjs → ng-forge-dynamic-forms-container-utils-Bx1A5tg-.mjs} +2 -2
  7. package/fesm2022/{ng-forge-dynamic-forms-container-utils-BTiqZ8Np.mjs.map → ng-forge-dynamic-forms-container-utils-Bx1A5tg-.mjs.map} +1 -1
  8. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-mdfySFjX.mjs → ng-forge-dynamic-forms-css-wrapper.component-Q2g56KUM.mjs} +5 -5
  9. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-Q2g56KUM.mjs.map +1 -0
  10. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-Bt3kvlOY.mjs → ng-forge-dynamic-forms-emit-initialization-9l5gPNQc.mjs} +2 -2
  11. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-Bt3kvlOY.mjs.map → ng-forge-dynamic-forms-emit-initialization-9l5gPNQc.mjs.map} +1 -1
  12. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-BxswrhOc.mjs → ng-forge-dynamic-forms-group-field.component-DqnhddRH.mjs} +36 -12
  13. package/fesm2022/ng-forge-dynamic-forms-group-field.component-DqnhddRH.mjs.map +1 -0
  14. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +936 -54
  15. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  16. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-QdV2vcCx.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-Be1Lk0qR.mjs} +2423 -466
  17. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-Be1Lk0qR.mjs.map +1 -0
  18. package/fesm2022/ng-forge-dynamic-forms-page-field.component-n4KTMOTm.mjs +114 -0
  19. package/fesm2022/ng-forge-dynamic-forms-page-field.component-n4KTMOTm.mjs.map +1 -0
  20. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs +34 -0
  21. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs.map +1 -0
  22. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-Cf1mEKVW.mjs → ng-forge-dynamic-forms-text-field.component-C14mlROS.mjs} +5 -5
  23. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-Cf1mEKVW.mjs.map → ng-forge-dynamic-forms-text-field.component-C14mlROS.mjs.map} +1 -1
  24. package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
  25. package/package.json +2 -2
  26. package/types/ng-forge-dynamic-forms-integration.d.ts +587 -11
  27. package/types/ng-forge-dynamic-forms.d.ts +3841 -2428
  28. package/fesm2022/ng-forge-dynamic-forms-array-field.component-CltYGHg9.mjs.map +0 -1
  29. package/fesm2022/ng-forge-dynamic-forms-container-field.component-D4Fpr2ri.mjs.map +0 -1
  30. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-mdfySFjX.mjs.map +0 -1
  31. package/fesm2022/ng-forge-dynamic-forms-group-field.component-BxswrhOc.mjs.map +0 -1
  32. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-QdV2vcCx.mjs.map +0 -1
  33. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BHNVryIq.mjs +0 -110
  34. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BHNVryIq.mjs.map +0 -1
  35. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BIvEUoia.mjs +0 -34
  36. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BIvEUoia.mjs.map +0 -1
package/README.md CHANGED
@@ -20,6 +20,7 @@ Core library for building type-safe, dynamic Angular forms with signal forms int
20
20
 
21
21
  | @ng-forge/dynamic-forms | Angular |
22
22
  | ----------------------- | ------------- |
23
+ | 0.9.x | >=21.2.0 |
23
24
  | 0.8.x | >=21.2.0 |
24
25
  | 0.7.x | >=21.2.0 |
25
26
  | 0.6.x | >=21.1.0 |
@@ -1,10 +1,62 @@
1
1
  import * as i0 from '@angular/core';
2
- import { computed, runInInjectionContext, Injector, linkedSignal, inject, DestroyRef, EnvironmentInjector, input, signal, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { R as RootFormRegistryService, g as isEqual, m as mapFieldToInputs, F as FIELD_SIGNAL_CONTEXT, A as ARRAY_CONTEXT, j as createRenderReadySignal, D as DynamicFormLogger, i as injectFieldRegistry, E as EventBus, k as ARRAY_TEMPLATE_REGISTRY, l as ARRAY_ITEM_ID_GENERATOR, n as getNormalizedArrayMetadata, o as getFieldValueHandling, p as getFieldDefaultValue, q as isGroupField, f as DfFieldOutlet, s as createArrayItemIdGenerator } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-QdV2vcCx.mjs';
2
+ import { signal, computed, runInInjectionContext, Injector, linkedSignal, inject, DestroyRef, EnvironmentInjector, input, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { O as RootFormRegistryService, ax as isEqual, aL as mapFieldToInputs, v as FIELD_SIGNAL_CONTEXT, e as ARRAY_CONTEXT, G as GROUP_CONTEXT, a5 as createHiddenSignal, a6 as createRenderReadySignal, s as DynamicFormLogger, ao as injectFieldRegistry, ap as injectFieldSignalContext, E as EventBus, g as ArrayItemRegistryService, ak as getNormalizedArrayMetadata, aj as getFieldValueHandling, ai as getFieldDefaultValue, aA as isGroupField, o as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-Be1Lk0qR.mjs';
4
4
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
5
  import { of, from, map, catchError, forkJoin, filter, firstValueFrom, tap } from 'rxjs';
6
6
  import { explicitEffect } from 'ngxtension/explicit-effect';
7
- import { e as emitComponentInitialized } from './ng-forge-dynamic-forms-emit-initialization-Bt3kvlOY.mjs';
7
+ import { e as emitComponentInitialized } from './ng-forge-dynamic-forms-emit-initialization-9l5gPNQc.mjs';
8
+
9
+ class ArrayFieldStateMachine {
10
+ _state = signal('idle', ...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
11
+ _runId = signal(0, ...(ngDevMode ? [{ debugName: "_runId" }] : /* istanbul ignore next */ []));
12
+ lastEmittedRunId = -1;
13
+ _hasEverEmitted = false;
14
+ state = this._state.asReadonly();
15
+ runId = this._runId.asReadonly();
16
+ /** True after the first `componentInitialized` pulse has fired for this component. */
17
+ get hasEverEmitted() {
18
+ return this._hasEverEmitted;
19
+ }
20
+ dispatch(op) {
21
+ const nextId = this._runId() + 1;
22
+ this._runId.set(nextId);
23
+ switch (op.kind) {
24
+ case 'clear':
25
+ case 'initial':
26
+ case 'recreate':
27
+ this._state.set('pending');
28
+ break;
29
+ case 'append':
30
+ // append doesn't reset state — mirrors today's behavior where
31
+ // appendItems leaves the init-cycle tracking alone.
32
+ break;
33
+ }
34
+ return {
35
+ id: nextId,
36
+ isStale: () => this._runId() !== nextId,
37
+ resolve: () => {
38
+ if (this._runId() === nextId && this._state() === 'pending') {
39
+ this._state.set('settled');
40
+ }
41
+ },
42
+ };
43
+ }
44
+ /**
45
+ * Edge-trigger for the emit-effect. Returns true exactly once per `settled` run
46
+ * when `allReady` is true. Side-effect: records the run as emitted and transitions
47
+ * the state back to `idle`. Subsequent calls without a fresh dispatch return false.
48
+ */
49
+ shouldEmit(allReady) {
50
+ if (this._state() !== 'settled' || !allReady)
51
+ return false;
52
+ if (this.lastEmittedRunId === this._runId())
53
+ return false;
54
+ this.lastEmittedRunId = this._runId();
55
+ this._hasEverEmitted = true;
56
+ this._state.set('idle');
57
+ return true;
58
+ }
59
+ }
8
60
 
9
61
  /**
10
62
  * Safely extracts an array value from a model object.
@@ -173,9 +225,22 @@ function createItemInjector(options) {
173
225
  deps: [Injector],
174
226
  },
175
227
  { provide: ARRAY_CONTEXT, useValue: arrayContext },
228
+ // Reset GROUP_CONTEXT at the array boundary: inner groups inside each
229
+ // item start their path fresh, matching how value-derivation and
230
+ // property-derivation collectors key entries
231
+ // (`{arrayKey}.$.{innerGroupPath}.{fieldKey}`). Without this, an outer
232
+ // group around the array would leak into descendants' DOM IDs and
233
+ // override-store keys, drifting from the collector.
234
+ { provide: GROUP_CONTEXT, useValue: EMPTY_GROUP_CONTEXT },
176
235
  ],
177
236
  });
178
237
  }
238
+ /**
239
+ * Sentinel `GroupContext` provided at array item boundaries. Static `signal('')`
240
+ * means `applyGroupPrefix` no-ops (empty path) and any inner group composes its
241
+ * own path from this empty base.
242
+ */
243
+ const EMPTY_GROUP_CONTEXT = { groupPath: signal('') };
179
244
 
180
245
  /**
181
246
  * Resolves a single array item with all its fields for declarative rendering.
@@ -185,12 +250,14 @@ function createItemInjector(options) {
185
250
  * Supports multiple sibling templates (e.g., name + email without a wrapper).
186
251
  */
187
252
  function resolveArrayItem(options) {
188
- const { index, templates, arrayField, itemPositionMap, parentFieldSignalContext, parentInjector, registry, destroyRef, loadTypeComponent, generateItemId, primitiveFieldKey, } = options;
253
+ const { index, templates, arrayField, itemPositionMap, parentFieldSignalContext, parentInjector, registry, destroyRef, loadTypeComponent, generateItemId, existingItemId, primitiveFieldKey, } = options;
189
254
  if (templates.length === 0) {
190
255
  return of(undefined);
191
256
  }
192
- // Generate ONE id for this array item (shared by all fields for tracking)
193
- const itemId = generateItemId();
257
+ // Generate ONE id for this array item (shared by all fields for tracking), unless
258
+ // the caller is rehydrating an existing item — in which case reuse its id so the
259
+ // form-scoped template registry continues to map cleanly.
260
+ const itemId = existingItemId ?? generateItemId();
194
261
  // O(1) position lookup via Map instead of O(n) indexOf()
195
262
  const indexSignal = linkedSignal(() => {
196
263
  const positionMap = itemPositionMap();
@@ -225,6 +292,7 @@ function resolveArrayItem(options) {
225
292
  injector,
226
293
  inputs,
227
294
  renderReady: createRenderReadySignal(inputs, registry.get(template.type)),
295
+ hidden: createHiddenSignal(inputs),
228
296
  };
229
297
  }), catchError((error) => {
230
298
  if (!destroyRef.destroyed) {
@@ -319,13 +387,21 @@ class ArrayFieldComponent {
319
387
  // ─────────────────────────────────────────────────────────────────────────────
320
388
  destroyRef = inject(DestroyRef);
321
389
  fieldRegistry = injectFieldRegistry();
322
- parentFieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
390
+ parentFieldSignalContext = injectFieldSignalContext();
323
391
  parentInjector = inject(Injector);
324
392
  environmentInjector = inject(EnvironmentInjector);
325
393
  eventBus = inject(EventBus);
326
394
  logger = inject(DynamicFormLogger);
327
- templateRegistry = inject(ARRAY_TEMPLATE_REGISTRY);
328
- generateItemId = inject(ARRAY_ITEM_ID_GENERATOR);
395
+ arrayItemRegistry = inject(ArrayItemRegistryService);
396
+ // Form-scoped slot keyed by the array's path. Survives this component's lifecycle so that
397
+ // `@if`-gated arrays can be destroyed and recreated without losing dynamically-added items.
398
+ // NOTE: keyed by `field().key` for now — nested arrays with the same key in different scopes
399
+ // would currently collide. Today's per-component-provider model has the same constraint
400
+ // (no nested-array hide/show tests exist). Path-aware keying is a follow-up.
401
+ // `field()` is `input.required`, so the slot reference is resolved lazily.
402
+ slot = computed(() => this.arrayItemRegistry.slotFor(this.field().key), ...(ngDevMode ? [{ debugName: "slot" }] : /* istanbul ignore next */ []));
403
+ /** Bound id-generator reference threaded to `resolveArrayItem` so callsites stay terse. */
404
+ generateItemId = () => this.slot().nextId();
329
405
  // ─────────────────────────────────────────────────────────────────────────────
330
406
  // Inputs
331
407
  // ─────────────────────────────────────────────────────────────────────────────
@@ -453,9 +529,9 @@ class ArrayFieldComponent {
453
529
  // State Signals
454
530
  // ─────────────────────────────────────────────────────────────────────────────
455
531
  resolvedItemsSignal = signal([], ...(ngDevMode ? [{ debugName: "resolvedItemsSignal" }] : /* istanbul ignore next */ []));
456
- updateVersion = signal(0, ...(ngDevMode ? [{ debugName: "updateVersion" }] : /* istanbul ignore next */ []));
457
- pendingInitializationCycle = signal(null, ...(ngDevMode ? [{ debugName: "pendingInitializationCycle" }] : /* istanbul ignore next */ []));
458
- settledInitializationCycle = signal(null, ...(ngDevMode ? [{ debugName: "settledInitializationCycle" }] : /* istanbul ignore next */ []));
532
+ // Explicit state machine for async resolution + componentInitialized emission.
533
+ // Replaces the prior updateVersion + pendingInitializationCycle + settledInitializationCycle trio.
534
+ fsm = new ArrayFieldStateMachine();
459
535
  /**
460
536
  * Map of item IDs to their current positions. O(1) lookup vs O(n) indexOf().
461
537
  * Used by child linkedSignals to reactively track their position in the array.
@@ -505,10 +581,9 @@ class ArrayFieldComponent {
505
581
  explicitEffect([this.arrayFieldTrees], ([fieldTrees]) => {
506
582
  this.performDifferentialUpdate(fieldTrees);
507
583
  });
508
- explicitEffect([this.pendingInitializationCycle, this.settledInitializationCycle, this.allResolvedFieldsRenderReady], ([pending, settled, allReady]) => {
509
- if (pending !== null && settled === pending && allReady) {
584
+ explicitEffect([this.fsm.state, this.allResolvedFieldsRenderReady], ([, allReady]) => {
585
+ if (this.fsm.shouldEmit(allReady)) {
510
586
  emitComponentInitialized(this.eventBus, 'array', this.field().key, this.parentInjector);
511
- this.pendingInitializationCycle.set(null);
512
587
  }
513
588
  });
514
589
  }
@@ -594,9 +669,9 @@ class ArrayFieldComponent {
594
669
  }
595
670
  }
596
671
  }
597
- // Increment version and create resolved item BEFORE updating value
598
- this.updateVersion.update((v) => v + 1);
599
- const currentVersion = this.updateVersion();
672
+ // Dispatch an append run — bumps runId so any in-flight differential update
673
+ // becomes stale, but state stays in its current phase.
674
+ const run = this.fsm.dispatch({ kind: 'append' });
600
675
  // Append auto-remove button to resolution templates (for rendering only)
601
676
  const resolveTemplates = this.withAutoRemove(templates);
602
677
  // Resolve the new item
@@ -616,11 +691,13 @@ class ArrayFieldComponent {
616
691
  this.logger.error(`Failed to resolve array item at index ${insertIndex}:`, error);
617
692
  return of(undefined);
618
693
  })));
619
- if (currentVersion !== this.updateVersion() || !resolvedItem) {
694
+ if (run.isStale() || !resolvedItem) {
620
695
  return;
621
696
  }
622
697
  // Store the template used for this item so it can be re-used during recreate operations
623
- this.templateRegistry.set(resolvedItem.id, templates);
698
+ this.slot().templates.set(resolvedItem.id, templates);
699
+ // Mirror the insert into slot.itemOrder so survival across hide/show stays consistent.
700
+ this.slot().itemOrder.splice(insertIndex, 0, resolvedItem.id);
624
701
  // Insert resolved item at correct position
625
702
  this.resolvedItemsSignal.update((current) => {
626
703
  const newItems = [...current];
@@ -638,31 +715,43 @@ class ArrayFieldComponent {
638
715
  performDifferentialUpdate(fieldTrees) {
639
716
  const resolvedItems = this.resolvedItemsSignal();
640
717
  const operation = determineDifferentialOperation(resolvedItems, fieldTrees.length);
641
- // Only increment version for operations that do actual work.
642
- // "none" operations (value-only changes) should not interfere with async add operations.
718
+ // "none" operations (value-only changes) don't kick the state machine — they shouldn't
719
+ // interfere with in-flight async resolutions.
643
720
  if (operation.type === 'none') {
644
721
  return;
645
722
  }
646
- this.updateVersion.update((v) => v + 1);
647
- const currentVersion = this.updateVersion();
648
723
  switch (operation.type) {
649
- case 'clear':
724
+ case 'clear': {
650
725
  // Clean up template registry when all items are removed
651
- this.templateRegistry.clear();
726
+ this.slot().templates.clear();
652
727
  this.resolvedItemsSignal.set([]);
653
- if (resolvedItems.length === 0 && this.pendingInitializationCycle() === null && this.settledInitializationCycle() === null) {
654
- this.pendingInitializationCycle.set(currentVersion);
655
- this.settledInitializationCycle.set(currentVersion);
728
+ const run = this.fsm.dispatch({ kind: 'clear' });
729
+ // First-ever clear with no items: synthesize a resolve so the emit-effect fires
730
+ // the init pulse (matches the form-starts-empty case from the prior implementation).
731
+ if (resolvedItems.length === 0 && !this.fsm.hasEverEmitted) {
732
+ run.resolve();
656
733
  }
657
734
  break;
658
- case 'initial':
659
- this.pendingInitializationCycle.set(currentVersion);
660
- this.settledInitializationCycle.set(null);
661
- void this.resolveAllItems(fieldTrees, currentVersion);
735
+ }
736
+ case 'initial': {
737
+ // If the slot has surviving state from a prior ArrayFieldComponent instance (same
738
+ // form, same array path, just a different render cycle — e.g. an `@if`-gated array
739
+ // that was hidden then re-shown), reuse the stored templates AND ids so dynamically-
740
+ // added items rebuild correctly. When the slot is empty this collapses to a normal
741
+ // initial resolve via Priority 2/3.
742
+ const slot = this.slot();
743
+ const useSurvival = slot.itemOrder.length === fieldTrees.length && fieldTrees.length > 0;
744
+ const positionalTemplates = useSurvival ? slot.itemOrder.map((id) => slot.templates.get(id)) : undefined;
745
+ const existingItemIds = useSurvival ? slot.itemOrder.slice() : undefined;
746
+ const run = this.fsm.dispatch({ kind: 'initial' });
747
+ void this.resolveAllItems(fieldTrees, run, positionalTemplates, existingItemIds);
662
748
  break;
663
- case 'append':
664
- void this.appendItems(fieldTrees, operation.startIndex, operation.endIndex, currentVersion);
749
+ }
750
+ case 'append': {
751
+ const run = this.fsm.dispatch({ kind: 'append' });
752
+ void this.appendItems(fieldTrees, operation.startIndex, operation.endIndex, run);
665
753
  break;
754
+ }
666
755
  case 'recreate': {
667
756
  // Capture templates by item ID so each item is recreated with its original template,
668
757
  // even after move operations that change positions. Registry entries (from dynamic adds
@@ -670,49 +759,53 @@ class ArrayFieldComponent {
670
759
  const positionalTemplates = resolvedItems.map((item) => {
671
760
  // Registry entries (from dynamic adds or moves) take priority;
672
761
  // unmoved initial items return undefined → falls through to itemTemplates[idx] during resolve.
673
- return this.templateRegistry.get(item.id);
762
+ return this.slot().templates.get(item.id);
674
763
  });
675
764
  this.resolvedItemsSignal.set([]);
676
- this.pendingInitializationCycle.set(currentVersion);
677
- this.settledInitializationCycle.set(null);
678
- void this.resolveAllItems(fieldTrees, currentVersion, positionalTemplates);
765
+ const run = this.fsm.dispatch({ kind: 'recreate' });
766
+ void this.resolveAllItems(fieldTrees, run, positionalTemplates);
679
767
  break;
680
768
  }
681
769
  }
682
770
  }
683
- async resolveAllItems(fieldTrees, updateId, positionalTemplates) {
771
+ async resolveAllItems(fieldTrees, run, positionalTemplates, existingItemIds) {
684
772
  if (fieldTrees.length === 0) {
685
773
  this.resolvedItemsSignal.set([]);
686
774
  return;
687
775
  }
688
776
  // Wrap each item observable to catch individual errors
689
- const safeItemObservables = fieldTrees.map((_, i) => this.createResolveItemObservable(i, positionalTemplates?.[i]).pipe(catchError((error) => {
777
+ const safeItemObservables = fieldTrees.map((_, i) => this.createResolveItemObservable(i, positionalTemplates?.[i], existingItemIds?.[i]).pipe(catchError((error) => {
690
778
  this.logger.error(`Failed to resolve array item at index ${i}:`, error);
691
779
  return of(undefined);
692
780
  })));
693
781
  try {
694
782
  const items = await firstValueFrom(forkJoin(safeItemObservables).pipe(map((items) => items.filter((item) => item !== undefined))));
695
- if (updateId === this.updateVersion()) {
783
+ if (!run.isStale()) {
696
784
  // Update template registry with new item IDs for dynamically added items
697
785
  if (positionalTemplates) {
698
786
  // Clean up old entries and add new ones
699
787
  const newItemIds = new Set(items.map((item) => item.id));
700
- for (const existingId of this.templateRegistry.keys()) {
788
+ for (const existingId of this.slot().templates.keys()) {
701
789
  if (!newItemIds.has(existingId)) {
702
- this.templateRegistry.delete(existingId);
790
+ this.slot().templates.delete(existingId);
703
791
  }
704
792
  }
705
793
  items.forEach((item, idx) => {
706
794
  const template = positionalTemplates[idx];
707
795
  if (template) {
708
- this.templateRegistry.set(item.id, template);
796
+ this.slot().templates.set(item.id, template);
709
797
  }
710
798
  });
711
799
  }
800
+ // Mirror the resolved item ids into the slot's itemOrder so a future component
801
+ // recreate (e.g. after an @if hide cycle) can rehydrate via the survival path.
802
+ const slot = this.slot();
803
+ slot.itemOrder.length = 0;
804
+ for (const item of items)
805
+ slot.itemOrder.push(item.id);
712
806
  this.resolvedItemsSignal.set(items);
713
- if (this.pendingInitializationCycle() === updateId) {
714
- this.settledInitializationCycle.set(updateId);
715
- }
807
+ // Mark this run settled so the emit-effect can fire componentInitialized once allReady is true.
808
+ run.resolve();
716
809
  }
717
810
  }
718
811
  catch (err) {
@@ -720,7 +813,7 @@ class ArrayFieldComponent {
720
813
  this.resolvedItemsSignal.set([]);
721
814
  }
722
815
  }
723
- async appendItems(fieldTrees, startIndex, endIndex, updateId) {
816
+ async appendItems(fieldTrees, startIndex, endIndex, run) {
724
817
  const itemsToResolve = fieldTrees.slice(startIndex, endIndex);
725
818
  if (itemsToResolve.length === 0)
726
819
  return;
@@ -734,7 +827,10 @@ class ArrayFieldComponent {
734
827
  });
735
828
  try {
736
829
  const newItems = await firstValueFrom(forkJoin(safeItemObservables).pipe(map((items) => items.filter((item) => item !== undefined))));
737
- if (updateId === this.updateVersion()) {
830
+ if (!run.isStale()) {
831
+ const slot = this.slot();
832
+ for (const item of newItems)
833
+ slot.itemOrder.push(item.id);
738
834
  this.resolvedItemsSignal.update((current) => [...current, ...newItems]);
739
835
  }
740
836
  }
@@ -755,7 +851,7 @@ class ArrayFieldComponent {
755
851
  * Priority 1.
756
852
  * 4. Return undefined (item cannot be resolved without a template)
757
853
  */
758
- createResolveItemObservable(index, overrideTemplate) {
854
+ createResolveItemObservable(index, overrideTemplate, existingItemId) {
759
855
  const itemTemplates = this.itemTemplates();
760
856
  const primitiveKey = this.primitiveFieldKey();
761
857
  // Priority 1: Use override template (from recreate with stored templates)
@@ -771,6 +867,7 @@ class ArrayFieldComponent {
771
867
  destroyRef: this.destroyRef,
772
868
  loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
773
869
  generateItemId: this.generateItemId,
870
+ existingItemId,
774
871
  primitiveFieldKey: primitiveKey,
775
872
  });
776
873
  }
@@ -788,6 +885,7 @@ class ArrayFieldComponent {
788
885
  destroyRef: this.destroyRef,
789
886
  loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
790
887
  generateItemId: this.generateItemId,
888
+ existingItemId,
791
889
  primitiveFieldKey: primitiveKey,
792
890
  });
793
891
  }
@@ -806,12 +904,13 @@ class ArrayFieldComponent {
806
904
  destroyRef: this.destroyRef,
807
905
  loadTypeComponent: (type) => this.fieldRegistry.loadTypeComponent(type),
808
906
  generateItemId: this.generateItemId,
907
+ existingItemId,
809
908
  primitiveFieldKey: primitiveKey,
810
909
  }).pipe(tap((item) => {
811
910
  // Register the fallback template against the generated item id so subsequent
812
911
  // recreates hit Priority 1 instead of re-resolving via this branch.
813
912
  if (item)
814
- this.templateRegistry.set(item.id, fallback);
913
+ this.slot().templates.set(item.id, fallback);
815
914
  }));
816
915
  }
817
916
  // Priority 4: no template available. Full-API arrays are positional by design —
@@ -881,8 +980,8 @@ class ArrayFieldComponent {
881
980
  const hi = Math.max(fromIndex, toIndex);
882
981
  for (let i = lo; i <= hi; i++) {
883
982
  const item = currentItems[i];
884
- if (item && !this.templateRegistry.has(item.id) && i < rawTemplates.length) {
885
- this.templateRegistry.set(item.id, rawTemplates[i]);
983
+ if (item && !this.slot().templates.has(item.id) && i < rawTemplates.length) {
984
+ this.slot().templates.set(item.id, rawTemplates[i]);
886
985
  }
887
986
  }
888
987
  // Reorder resolvedItems — splice preserves object identity (no destroy/recreate)
@@ -892,6 +991,8 @@ class ArrayFieldComponent {
892
991
  newItems.splice(toIndex, 0, moved);
893
992
  return newItems;
894
993
  });
994
+ // Keep slot.itemOrder in sync so the post-recreate survival path sees the moved order.
995
+ this.slot().moveItem(fromIndex, toIndex);
895
996
  // Reorder form value array the same way
896
997
  const newArray = [...currentArray];
897
998
  const [movedValue] = newArray.splice(fromIndex, 1);
@@ -935,8 +1036,11 @@ class ArrayFieldComponent {
935
1036
  // no longer exists, params returns undefined, cancelling the pending validation automatically.
936
1037
  const removedItem = this.resolvedItemsSignal()[removeIndex];
937
1038
  if (removedItem) {
938
- this.templateRegistry.delete(removedItem.id);
1039
+ this.slot().templates.delete(removedItem.id);
939
1040
  }
1041
+ // Keep slot.itemOrder in sync so the post-recreate survival path doesn't try to
1042
+ // restore an id that's no longer in the form value.
1043
+ this.slot().removeAt(removeIndex);
940
1044
  this.resolvedItemsSignal.update((current) => {
941
1045
  const newItems = [...current];
942
1046
  newItems.splice(removeIndex, 1);
@@ -949,13 +1053,8 @@ class ArrayFieldComponent {
949
1053
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
950
1054
  parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
951
1055
  }
952
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
953
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", 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()" } }, providers: [
954
- // Each array gets its own template registry to track templates used for dynamically added items
955
- { provide: ARRAY_TEMPLATE_REGISTRY, useFactory: () => new Map() },
956
- // Each array gets its own ID generator for SSR hydration compatibility
957
- { provide: ARRAY_ITEM_ID_GENERATOR, useFactory: createArrayItemIdGenerator },
958
- ], ngImport: i0, template: `
1056
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1057
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
959
1058
  @for (item of resolvedItems(); track item.id; let i = $index) {
960
1059
  <div
961
1060
  class="df-array-item"
@@ -965,13 +1064,15 @@ class ArrayFieldComponent {
965
1064
  [attr.data-array-item-index]="i"
966
1065
  >
967
1066
  @for (field of item.fields; track $index) {
968
- <ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
1067
+ @if (!field.hidden()) {
1068
+ <ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
1069
+ }
969
1070
  }
970
1071
  </div>
971
1072
  }
972
- `, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-array-item-gap: var(--df-grid-row-gap);--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);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);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);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.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),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);--df-grid-row-gap: var(--df-grid-row-gap-md)}.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);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.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);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.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 });
1073
+ `, 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 });
973
1074
  }
974
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ArrayFieldComponent, decorators: [{
1075
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayFieldComponent, decorators: [{
975
1076
  type: Component,
976
1077
  args: [{ selector: 'array-field', imports: [DfFieldOutlet], template: `
977
1078
  @for (item of resolvedItems(); track item.id; let i = $index) {
@@ -983,7 +1084,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
983
1084
  [attr.data-array-item-index]="i"
984
1085
  >
985
1086
  @for (field of item.fields; track $index) {
986
- <ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
1087
+ @if (!field.hidden()) {
1088
+ <ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
1089
+ }
987
1090
  }
988
1091
  </div>
989
1092
  }
@@ -993,13 +1096,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
993
1096
  '[attr.aria-hidden]': 'hidden() || null',
994
1097
  '[id]': '`${key()}`',
995
1098
  '[attr.data-testid]': 'key()',
996
- }, providers: [
997
- // Each array gets its own template registry to track templates used for dynamically added items
998
- { provide: ARRAY_TEMPLATE_REGISTRY, useFactory: () => new Map() },
999
- // Each array gets its own ID generator for SSR hydration compatibility
1000
- { provide: ARRAY_ITEM_ID_GENERATOR, useFactory: createArrayItemIdGenerator },
1001
- ], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-array-item-gap: var(--df-grid-row-gap);--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);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);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);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.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),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);--df-grid-row-gap: var(--df-grid-row-gap-md)}.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);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.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);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.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"] }]
1099
+ }, 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"] }]
1002
1100
  }], 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 }] }] } });
1003
1101
 
1004
1102
  export { ArrayFieldComponent, ArrayFieldComponent as default };
1005
- //# sourceMappingURL=ng-forge-dynamic-forms-array-field.component-CltYGHg9.mjs.map
1103
+ //# sourceMappingURL=ng-forge-dynamic-forms-array-field.component-Tw49K5kz.mjs.map