@ng-forge/dynamic-forms 1.3.0-next.1 → 1.3.0-next.3

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 (92) hide show
  1. package/fesm2022/ng-forge-dynamic-forms-active-page-initialized.event-CiKN-mcc.mjs +12 -0
  2. package/fesm2022/ng-forge-dynamic-forms-active-page-initialized.event-CiKN-mcc.mjs.map +1 -0
  3. package/fesm2022/ng-forge-dynamic-forms-array-field.component-Dv9mPnJ3.mjs +770 -0
  4. package/fesm2022/ng-forge-dynamic-forms-array-field.component-Dv9mPnJ3.mjs.map +1 -0
  5. package/fesm2022/ng-forge-dynamic-forms-array-item-registry.service-D-AxVLdk.mjs +83 -0
  6. package/fesm2022/ng-forge-dynamic-forms-array-item-registry.service-D-AxVLdk.mjs.map +1 -0
  7. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-EuYCbquo.mjs +1887 -0
  8. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-EuYCbquo.mjs.map +1 -0
  9. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-2wQK_wPp.mjs +954 -0
  10. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-2wQK_wPp.mjs.map +1 -0
  11. package/fesm2022/ng-forge-dynamic-forms-container-field-processors-C_JRvaVt.mjs +117 -0
  12. package/fesm2022/ng-forge-dynamic-forms-container-field-processors-C_JRvaVt.mjs.map +1 -0
  13. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Bg7Win9n.mjs +228 -0
  14. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Bg7Win9n.mjs.map +1 -0
  15. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-R2yc-H0T.mjs +102 -0
  16. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-R2yc-H0T.mjs.map +1 -0
  17. package/fesm2022/ng-forge-dynamic-forms-debounce-CG3uP5B-.mjs +4 -0
  18. package/fesm2022/{ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs.map → ng-forge-dynamic-forms-debounce-CG3uP5B-.mjs.map} +1 -1
  19. package/fesm2022/ng-forge-dynamic-forms-default-value-D5M4MNE-.mjs +77 -0
  20. package/fesm2022/ng-forge-dynamic-forms-default-value-D5M4MNE-.mjs.map +1 -0
  21. package/fesm2022/ng-forge-dynamic-forms-dev-mode-4rbYBBo_.mjs +4 -0
  22. package/fesm2022/ng-forge-dynamic-forms-dev-mode-4rbYBBo_.mjs.map +1 -0
  23. package/fesm2022/ng-forge-dynamic-forms-df-field-outlet.directive-CQsDMV-s.mjs +644 -0
  24. package/fesm2022/ng-forge-dynamic-forms-df-field-outlet.directive-CQsDMV-s.mjs.map +1 -0
  25. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-DKgkpIpG.mjs +56 -0
  26. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-DKgkpIpG.mjs.map +1 -0
  27. package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dd-gHJ_7.mjs +38 -0
  28. package/fesm2022/{ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs.map → ng-forge-dynamic-forms-form-schema.service-Dd-gHJ_7.mjs.map} +1 -1
  29. package/fesm2022/ng-forge-dynamic-forms-form-state-manager-DcOXRFRL.mjs +1522 -0
  30. package/fesm2022/ng-forge-dynamic-forms-form-state-manager-DcOXRFRL.mjs.map +1 -0
  31. package/fesm2022/ng-forge-dynamic-forms-group-field.component-BjZi0bhq.mjs +292 -0
  32. package/fesm2022/ng-forge-dynamic-forms-group-field.component-BjZi0bhq.mjs.map +1 -0
  33. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +1318 -843
  34. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  35. package/fesm2022/ng-forge-dynamic-forms-internal.mjs +3326 -3641
  36. package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
  37. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BRjQmWC9.mjs +295 -0
  38. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BRjQmWC9.mjs.map +1 -0
  39. package/fesm2022/ng-forge-dynamic-forms-resolve-field-ByGa5cpo.mjs +329 -0
  40. package/fesm2022/ng-forge-dynamic-forms-resolve-field-ByGa5cpo.mjs.map +1 -0
  41. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CuFycQb-.mjs +81 -0
  42. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CuFycQb-.mjs.map +1 -0
  43. package/fesm2022/ng-forge-dynamic-forms-schema-builder-C62afItJ.mjs +400 -0
  44. package/fesm2022/ng-forge-dynamic-forms-schema-builder-C62afItJ.mjs.map +1 -0
  45. package/fesm2022/ng-forge-dynamic-forms-schema-registry.service-axBV9FBH.mjs +45 -0
  46. package/fesm2022/ng-forge-dynamic-forms-schema-registry.service-axBV9FBH.mjs.map +1 -0
  47. package/fesm2022/ng-forge-dynamic-forms-schema.mjs +8 -14
  48. package/fesm2022/ng-forge-dynamic-forms-schema.mjs.map +1 -1
  49. package/fesm2022/ng-forge-dynamic-forms-testing.mjs +68 -73
  50. package/fesm2022/ng-forge-dynamic-forms-testing.mjs.map +1 -1
  51. package/fesm2022/ng-forge-dynamic-forms-text-field.component--MAh_h4Q.mjs +257 -0
  52. package/fesm2022/ng-forge-dynamic-forms-text-field.component--MAh_h4Q.mjs.map +1 -0
  53. package/fesm2022/ng-forge-dynamic-forms-validation-execution.token-BFyySKtp.mjs +8 -0
  54. package/fesm2022/ng-forge-dynamic-forms-validation-execution.token-BFyySKtp.mjs.map +1 -0
  55. package/fesm2022/ng-forge-dynamic-forms-with-logger-config-BAATqYY0.mjs +94 -0
  56. package/fesm2022/ng-forge-dynamic-forms-with-logger-config-BAATqYY0.mjs.map +1 -0
  57. package/fesm2022/ng-forge-dynamic-forms-wrapper-chain-BnntfNU1.mjs +115 -0
  58. package/fesm2022/ng-forge-dynamic-forms-wrapper-chain-BnntfNU1.mjs.map +1 -0
  59. package/fesm2022/ng-forge-dynamic-forms.mjs +2136 -3
  60. package/fesm2022/ng-forge-dynamic-forms.mjs.map +1 -1
  61. package/package.json +1 -1
  62. package/types/ng-forge-dynamic-forms-integration.d.ts +543 -415
  63. package/types/ng-forge-dynamic-forms-internal.d.ts +2319 -2436
  64. package/types/ng-forge-dynamic-forms-schema.d.ts +13 -16
  65. package/types/ng-forge-dynamic-forms-testing.d.ts +34 -37
  66. package/types/ng-forge-dynamic-forms.d.ts +735 -722
  67. package/fesm2022/ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs +0 -750
  68. package/fesm2022/ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs.map +0 -1
  69. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs +0 -2394
  70. package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs.map +0 -1
  71. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs +0 -1054
  72. package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs.map +0 -1
  73. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs +0 -86
  74. package/fesm2022/ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs.map +0 -1
  75. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs +0 -33
  76. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs.map +0 -1
  77. package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs +0 -17
  78. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs +0 -25
  79. package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs.map +0 -1
  80. package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs +0 -24
  81. package/fesm2022/ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs +0 -177
  82. package/fesm2022/ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs.map +0 -1
  83. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs +0 -5133
  84. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs.map +0 -1
  85. package/fesm2022/ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs +0 -176
  86. package/fesm2022/ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs.map +0 -1
  87. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs +0 -19
  88. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs.map +0 -1
  89. package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs +0 -570
  90. package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs.map +0 -1
  91. package/fesm2022/ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs +0 -126
  92. package/fesm2022/ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs.map +0 -1
@@ -0,0 +1,644 @@
1
+ import { i as renderWrapperChain, o as setInputIfDeclared, r as loadWrapperComponents, t as createWrapperAwareInjector } from "./ng-forge-dynamic-forms-wrapper-chain-BnntfNU1.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { DestroyRef, Directive, EnvironmentInjector, Injectable, InjectionToken, Injector, PLATFORM_ID, ViewContainerRef, afterNextRender, computed, inject, input, signal } from "@angular/core";
4
+ import { explicitEffect } from "ngxtension/explicit-effect";
5
+ import { derivedFrom } from "ngxtension/derived-from";
6
+ import { Observable, distinctUntilChanged, map, of, pipe, switchMap } from "rxjs";
7
+ import { ComponentInitializedEvent, DEFAULT_WRAPPERS, DynamicFormLogger, EventBus, FORM_OPTIONS, GROUP_CONTEXT, READONLY_FIELD_TREE_CACHE, WRAPPER_AUTO_ASSOCIATIONS, WRAPPER_COMPONENT_CACHE, WRAPPER_COMPONENT_LOAD_CACHE, WRAPPER_REGISTRY, getGridClassString, hasChildFields, isArrayField, isGenericContainerField, isGroupField, isPageField, isRowField, normalizeFieldsArray, toReadonlyFieldTreeCached, writeToFieldValue } from "@ng-forge/dynamic-forms/internal";
8
+ import { isPlatformBrowser } from "@angular/common";
9
+ import { takeUntilDestroyed, toObservable } from "@angular/core/rxjs-interop";
10
+ const FIELD_WINDOWING = new InjectionToken("FIELD_WINDOWING", {
11
+ providedIn: "root",
12
+ factory: () => ({
13
+ enabled: false,
14
+ eager: 12,
15
+ placeholderHeight: "4rem",
16
+ park: {
17
+ enabled: false,
18
+ margin: "100%"
19
+ }
20
+ })
21
+ });
22
+ function clampWindowSize(value, fallback) {
23
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
24
+ return Math.max(0, Math.floor(value));
25
+ }
26
+ const DEFAULT_MARGIN = "100%";
27
+ const ROOT_MARGIN_PART = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?(?:px|%)$/i;
28
+ function normalizeFieldParkingMargin(value, fallback = DEFAULT_MARGIN) {
29
+ const normalizedFallback = isValidRootMargin(fallback) ? fallback.trim() : DEFAULT_MARGIN;
30
+ if (!value || !isValidRootMargin(value)) return normalizedFallback;
31
+ return value.trim().split(/\s+/).join(" ");
32
+ }
33
+ function isValidRootMargin(value) {
34
+ const parts = value.trim().split(/\s+/);
35
+ return parts.length >= 1 && parts.length <= 4 && parts.every((part) => ROOT_MARGIN_PART.test(part));
36
+ }
37
+ function resolveFieldWindowing(global, perForm) {
38
+ if (perForm === void 0) return global;
39
+ if (typeof perForm === "boolean") return {
40
+ ...global,
41
+ enabled: perForm
42
+ };
43
+ return {
44
+ enabled: "eager" in perForm || "placeholderHeight" in perForm || Object.keys(perForm).length === 0 ? true : global.enabled,
45
+ eager: clampWindowSize(perForm.eager, global.eager),
46
+ placeholderHeight: perForm.placeholderHeight ?? global.placeholderHeight,
47
+ park: resolveFieldParking(global.park, perForm.park)
48
+ };
49
+ }
50
+ function resolveFieldParking(global, perForm) {
51
+ if (perForm === void 0) return global;
52
+ if (typeof perForm === "boolean") return {
53
+ ...global,
54
+ enabled: perForm
55
+ };
56
+ return {
57
+ enabled: true,
58
+ margin: normalizeFieldParkingMargin(perForm.margin, global.margin)
59
+ };
60
+ }
61
+ const ALWAYS_VISIBLE = of(true);
62
+ var FieldViewportObserver = class FieldViewportObserver {
63
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID)) && typeof IntersectionObserver !== "undefined";
64
+ observers = /* @__PURE__ */ new Map();
65
+ constructor() {
66
+ inject(DestroyRef).onDestroy(() => {
67
+ for (const { observer } of this.observers.values()) observer.disconnect();
68
+ this.observers.clear();
69
+ });
70
+ }
71
+ observe(el, rootMargin) {
72
+ if (!this.isBrowser) return ALWAYS_VISIBLE;
73
+ rootMargin = normalizeFieldParkingMargin(rootMargin);
74
+ return new Observable((subscriber) => {
75
+ const listener = (visible) => subscriber.next(visible);
76
+ this.startObserving(el, rootMargin, listener);
77
+ subscriber.next(true);
78
+ return () => this.stopObserving(el, rootMargin, listener);
79
+ }).pipe(distinctUntilChanged());
80
+ }
81
+ startObserving(el, rootMargin, listener) {
82
+ const entry = this.observerFor(rootMargin);
83
+ const listeners = entry.listeners.get(el);
84
+ if (listeners) {
85
+ listeners.add(listener);
86
+ return;
87
+ }
88
+ entry.listeners.set(el, /* @__PURE__ */ new Set([listener]));
89
+ entry.observer.observe(el);
90
+ }
91
+ stopObserving(el, rootMargin, listener) {
92
+ const entry = this.observers.get(rootMargin);
93
+ if (!entry) return;
94
+ const listeners = entry.listeners.get(el);
95
+ if (!listeners) return;
96
+ listeners.delete(listener);
97
+ if (listeners.size > 0) return;
98
+ entry.observer.unobserve(el);
99
+ entry.listeners.delete(el);
100
+ if (entry.listeners.size > 0) return;
101
+ entry.observer.disconnect();
102
+ this.observers.delete(rootMargin);
103
+ }
104
+ observerFor(rootMargin) {
105
+ const existing = this.observers.get(rootMargin);
106
+ if (existing) return existing;
107
+ const listeners = /* @__PURE__ */ new Map();
108
+ const entry = {
109
+ observer: new IntersectionObserver((entries) => {
110
+ for (const entry of entries) for (const listener of listeners.get(entry.target) ?? []) listener(entry.isIntersecting);
111
+ }, { rootMargin }),
112
+ listeners
113
+ };
114
+ this.observers.set(rootMargin, entry);
115
+ return entry;
116
+ }
117
+ static ɵfac = i0.ɵɵngDeclareFactory({
118
+ minVersion: "12.0.0",
119
+ version: "22.2.5",
120
+ ngImport: i0,
121
+ type: FieldViewportObserver,
122
+ deps: [],
123
+ target: i0.ɵɵFactoryTarget.Injectable
124
+ });
125
+ static ɵprov = i0.ɵɵngDeclareInjectable({
126
+ minVersion: "12.0.0",
127
+ version: "22.2.5",
128
+ ngImport: i0,
129
+ type: FieldViewportObserver
130
+ });
131
+ };
132
+ i0.ɵɵngDeclareClassMetadata({
133
+ minVersion: "12.0.0",
134
+ version: "22.2.5",
135
+ ngImport: i0,
136
+ type: FieldViewportObserver,
137
+ decorators: [{ type: Injectable }],
138
+ ctorParameters: () => []
139
+ });
140
+ const EMPTY_WRAPPERS = Object.freeze([]);
141
+ const FIELD_ERRORS_WRAPPER = "field-errors";
142
+ const FIELD_ERRORS_CONFIG = Object.freeze({ type: FIELD_ERRORS_WRAPPER });
143
+ function isSameWrapperChain(a, b) {
144
+ if (a === b) return true;
145
+ if (a.length !== b.length) return false;
146
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
147
+ return true;
148
+ }
149
+ function resolveWrappers(field, defaultWrappers, autoAssociations, wrapperRegistry) {
150
+ if (field.wrappers === null) return EMPTY_WRAPPERS;
151
+ const autoWrappers = field.skipAutoWrappers ? EMPTY_WRAPPERS : autoAssociations.get(field.type) ?? EMPTY_WRAPPERS;
152
+ const defaults = field.skipDefaultWrappers ? EMPTY_WRAPPERS : defaultWrappers ?? EMPTY_WRAPPERS;
153
+ const fieldLevel = field.wrappers ?? EMPTY_WRAPPERS;
154
+ const composed = [
155
+ ...autoWrappers,
156
+ ...defaults,
157
+ ...fieldLevel
158
+ ];
159
+ if (needsErrorWrapper(field, composed, wrapperRegistry)) composed.push(FIELD_ERRORS_CONFIG);
160
+ return composed.length === 0 ? EMPTY_WRAPPERS : composed;
161
+ }
162
+ const VALIDATABLE_CONTAINERS = /* @__PURE__ */ new Set(["group", "array"]);
163
+ function needsErrorWrapper(field, composed, wrapperRegistry) {
164
+ if (!VALIDATABLE_CONTAINERS.has(field.type)) return false;
165
+ if (!field.validators || field.validators.length === 0) return false;
166
+ return !composed.some((w) => w.type === FIELD_ERRORS_WRAPPER || wrapperRegistry.get(w.type)?.rendersFieldErrors);
167
+ }
168
+ function createWrapperChainController(opts) {
169
+ const deps = injectChainDeps();
170
+ const state = createStateSignal(opts);
171
+ const mounted = { value: null };
172
+ const detached = { value: null };
173
+ let refs = [];
174
+ buildEmissionStream(state, deps).pipe(takeUntilDestroyed(deps.destroyRef)).subscribe((emission) => {
175
+ try {
176
+ refs = applyEmission(emission, {
177
+ opts,
178
+ deps,
179
+ mounted,
180
+ refs,
181
+ detached
182
+ });
183
+ } catch (err) {
184
+ deps.logger.error("Wrapper chain render failed; tearing down partial state.", err);
185
+ opts.vcr().clear();
186
+ refs = [];
187
+ mounted.value = null;
188
+ detached.value?.destroy();
189
+ detached.value = null;
190
+ }
191
+ });
192
+ pushFieldInputsOnChange(opts, () => refs);
193
+ deps.destroyRef.onDestroy(() => {
194
+ detached.value?.destroy();
195
+ opts.vcr().clear();
196
+ });
197
+ }
198
+ function injectChainDeps() {
199
+ return {
200
+ registry: inject(WRAPPER_REGISTRY),
201
+ cache: inject(WRAPPER_COMPONENT_CACHE),
202
+ loadCache: inject(WRAPPER_COMPONENT_LOAD_CACHE),
203
+ logger: inject(DynamicFormLogger),
204
+ destroyRef: inject(DestroyRef),
205
+ environmentInjector: inject(EnvironmentInjector)
206
+ };
207
+ }
208
+ function createStateSignal(opts) {
209
+ return computed(() => ({
210
+ open: opts.gate?.() ?? true,
211
+ wrappers: opts.wrappers(),
212
+ rebuildKey: opts.rebuildKey?.()
213
+ }), { equal: (a, b) => a.open === b.open && a.rebuildKey === b.rebuildKey && isSameWrapperChain(a.wrappers, b.wrappers) });
214
+ }
215
+ function buildEmissionStream(state, deps) {
216
+ return toObservable(state).pipe(switchMap((s) => resolveLoadedWrappers(s, deps)));
217
+ }
218
+ function resolveLoadedWrappers(state, deps) {
219
+ if (!state.open) return of({
220
+ state,
221
+ loaded: null
222
+ });
223
+ if (state.wrappers.every((w) => deps.cache.has(w.type))) {
224
+ const loaded = state.wrappers.map((config) => ({
225
+ config,
226
+ component: deps.cache.get(config.type)
227
+ }));
228
+ return of({
229
+ state,
230
+ loaded
231
+ });
232
+ }
233
+ return loadWrapperComponents(state.wrappers, deps.registry, deps.cache, deps.logger, deps.loadCache).pipe(map((loaded) => ({
234
+ state,
235
+ loaded
236
+ })));
237
+ }
238
+ function applyEmission({ state, loaded }, ctx) {
239
+ const { opts, deps, mounted, refs } = ctx;
240
+ const vcr = opts.vcr();
241
+ const structurallyChanged = isStructurallyDifferent(mounted.value, state);
242
+ if (!structurallyChanged && !state.open) {
243
+ if (vcr.length > 0) ctx.detached.value = vcr.detach(0) ?? ctx.detached.value;
244
+ return refs;
245
+ }
246
+ if (state.open && !structurallyChanged && mounted.value !== null && ctx.detached.value) {
247
+ vcr.insert(ctx.detached.value);
248
+ ctx.detached.value = null;
249
+ return refs;
250
+ }
251
+ if (structurallyChanged && mounted.value !== null) {
252
+ const preservesDetachedOuterView = opts.beforeRebuild?.() === true;
253
+ if (ctx.detached.value && !preservesDetachedOuterView) ctx.detached.value.destroy();
254
+ ctx.detached.value = null;
255
+ vcr.clear();
256
+ mounted.value = null;
257
+ }
258
+ if (!state.open || loaded === null) return [];
259
+ if (mounted.value !== null) return refs;
260
+ const newRefs = renderWrapperChain({
261
+ outerContainer: vcr,
262
+ loadedWrappers: loaded,
263
+ environmentInjector: deps.environmentInjector,
264
+ logger: deps.logger,
265
+ fieldInputs: opts.fieldInputs?.(),
266
+ fieldInjector: opts.fieldInjector?.(),
267
+ outermostHostClasses: opts.outermostHostClasses?.(),
268
+ renderInnermost: opts.renderInnermost
269
+ });
270
+ mounted.value = {
271
+ wrappers: state.wrappers,
272
+ rebuildKey: state.rebuildKey
273
+ };
274
+ return newRefs;
275
+ }
276
+ function isStructurallyDifferent(mounted, next) {
277
+ if (mounted === null) return true;
278
+ if (mounted.rebuildKey !== next.rebuildKey) return true;
279
+ return !isSameWrapperChain(mounted.wrappers, next.wrappers);
280
+ }
281
+ function pushFieldInputsOnChange(opts, getRefs) {
282
+ if (!opts.fieldInputs) return;
283
+ explicitEffect([opts.fieldInputs], ([fi]) => {
284
+ for (const ref of getRefs()) setInputIfDeclared(ref, "fieldInputs", fi);
285
+ });
286
+ }
287
+ function buildFieldInputs(rawInputs, cache, fieldType, validationMessages) {
288
+ const fieldTreeCandidate = rawInputs["field"];
289
+ const hasFieldTree = fieldTreeCandidate !== void 0 && typeof fieldTreeCandidate === "function";
290
+ const readonlyField = hasFieldTree ? toReadonlyFieldTreeCached(cache, fieldTreeCandidate) : void 0;
291
+ const setValue = hasFieldTree ? (next) => writeToFieldValue(fieldTreeCandidate().value, next) : void 0;
292
+ return {
293
+ ...rawInputs,
294
+ type: fieldType,
295
+ field: readonlyField,
296
+ setValue,
297
+ ...validationMessages !== void 0 && { validationMessages }
298
+ };
299
+ }
300
+ const EMPTY_STATE = Object.freeze({ phase: "empty" });
301
+ var FieldComponentSlot = class {
302
+ state = signal(EMPTY_STATE, ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
303
+ lastInputs = null;
304
+ _parked = signal(false, ...ngDevMode ? [{ debugName: "_parked" }] : /* istanbul ignore next */ []);
305
+ phase = () => this.state().phase;
306
+ snapshot = () => this.state();
307
+ parked = this._parked.asReadonly();
308
+ mountOrReuse(slot, component, fieldInjector, environmentInjector, rawInputs, fieldInputs) {
309
+ const current = this.state();
310
+ if (current.phase !== "empty" && current.ref.componentType === component) {
311
+ slot.insert(current.ref.hostView);
312
+ this.lastInputs = null;
313
+ this.state.set(Object.freeze({
314
+ phase: "mounted",
315
+ ref: current.ref,
316
+ slot
317
+ }));
318
+ if (this._parked()) current.ref.changeDetectorRef.detach();
319
+ this.pushInputs(rawInputs, fieldInputs);
320
+ if (current.phase === "detached") this.replayFocus(current.focusSnapshot);
321
+ return;
322
+ }
323
+ if (current.phase !== "empty") current.ref.destroy();
324
+ this.lastInputs = null;
325
+ this._parked.set(false);
326
+ this.state.set(EMPTY_STATE);
327
+ const ref = slot.createComponent(component, {
328
+ environmentInjector,
329
+ injector: createWrapperAwareInjector(fieldInjector, slot.injector)
330
+ });
331
+ this.state.set(Object.freeze({
332
+ phase: "mounted",
333
+ ref,
334
+ slot
335
+ }));
336
+ this.pushInputs(rawInputs, fieldInputs);
337
+ }
338
+ pushInputs(rawInputs, fieldInputs) {
339
+ const current = this.state();
340
+ if (current.phase !== "mounted") return;
341
+ const prev = this.lastInputs;
342
+ if (prev === rawInputs) return;
343
+ for (const [key, value] of Object.entries(rawInputs)) {
344
+ if (prev && prev[key] === value) continue;
345
+ current.ref.setInput(key, value);
346
+ }
347
+ if (fieldInputs !== void 0) setInputIfDeclared(current.ref, "fieldInputs", fieldInputs);
348
+ this.lastInputs = rawInputs;
349
+ }
350
+ detach() {
351
+ const current = this.state();
352
+ if (current.phase !== "mounted") return false;
353
+ const focusSnapshot = this.captureFocus(current.ref);
354
+ const idx = current.slot.indexOf(current.ref.hostView);
355
+ if (idx >= 0) current.slot.detach(idx);
356
+ this.lastInputs = null;
357
+ this.state.set(Object.freeze({
358
+ phase: "detached",
359
+ ref: current.ref,
360
+ focusSnapshot
361
+ }));
362
+ return idx < 0;
363
+ }
364
+ park() {
365
+ const current = this.state();
366
+ if (current.phase === "empty" || this._parked()) return;
367
+ current.ref.changeDetectorRef.detach();
368
+ this._parked.set(true);
369
+ }
370
+ refresh() {
371
+ const current = this.state();
372
+ if (current.phase === "empty" || !this._parked()) return;
373
+ current.ref.changeDetectorRef.detectChanges();
374
+ }
375
+ unpark() {
376
+ const current = this.state();
377
+ if (current.phase === "empty" || !this._parked()) return;
378
+ current.ref.changeDetectorRef.reattach();
379
+ current.ref.changeDetectorRef.markForCheck();
380
+ this._parked.set(false);
381
+ }
382
+ destroyOnTeardown() {
383
+ const current = this.state();
384
+ if (current.phase === "detached") current.ref.destroy();
385
+ this.lastInputs = null;
386
+ this._parked.set(false);
387
+ this.state.set(EMPTY_STATE);
388
+ }
389
+ captureFocus(ref) {
390
+ const hostEl = ref.location.nativeElement;
391
+ const active = document.activeElement;
392
+ if (!(active instanceof HTMLElement) || !hostEl.contains(active)) return null;
393
+ const sel = active;
394
+ return {
395
+ element: active,
396
+ selectionStart: typeof sel.selectionStart === "number" ? sel.selectionStart : null,
397
+ selectionEnd: typeof sel.selectionEnd === "number" ? sel.selectionEnd : null
398
+ };
399
+ }
400
+ replayFocus(snap) {
401
+ if (!snap || !snap.element.isConnected) return;
402
+ snap.element.focus();
403
+ if (snap.selectionStart !== null && snap.selectionEnd !== null && "setSelectionRange" in snap.element) snap.element.setSelectionRange(snap.selectionStart, snap.selectionEnd);
404
+ }
405
+ };
406
+ function emitComponentInitialized(eventBus, componentType, componentKey, injector) {
407
+ const logger = injector.get(DynamicFormLogger);
408
+ afterNextRender(() => {
409
+ try {
410
+ eventBus.dispatch(ComponentInitializedEvent, componentType, componentKey);
411
+ } catch (error) {
412
+ logger.error(`Failed to emit initialization event for ${componentType} '${componentKey}'`, error);
413
+ }
414
+ }, { injector });
415
+ }
416
+ function initializationComponentKey(type, key) {
417
+ return `${type}:${key}`;
418
+ }
419
+ function initializationComponentPath(key, groupPath) {
420
+ return groupPath ? `${groupPath}.${key}` : key;
421
+ }
422
+ function initializationComponentType(field) {
423
+ if (isPageField(field)) return "page";
424
+ if (isGroupField(field)) return "group";
425
+ if (isArrayField(field)) return "array";
426
+ if (isRowField(field) || isGenericContainerField(field)) return "container";
427
+ }
428
+ function collectInitializingContainers(fields, groupPath = "") {
429
+ const identities = [];
430
+ for (const field of fields) {
431
+ if (field.hidden === true) continue;
432
+ const type = initializationComponentType(field);
433
+ const componentPath = initializationComponentPath(field.key, groupPath);
434
+ if (type && field.key) identities.push({
435
+ type,
436
+ path: componentPath
437
+ });
438
+ if (!hasChildFields(field)) continue;
439
+ const childGroupPath = isGroupField(field) ? componentPath : groupPath;
440
+ for (const child of normalizeFieldsArray(field.fields)) identities.push(...collectInitializingContainers(Array.isArray(child) ? child : [child], childGroupPath));
441
+ }
442
+ return identities;
443
+ }
444
+ function collectInitializingContainerKeys(fields, groupPath = "") {
445
+ return collectInitializingContainers(fields, groupPath).map(({ type, path }) => initializationComponentKey(type, path));
446
+ }
447
+ function computeContainerHostClasses(containerType, className) {
448
+ const base = `df-field df-${containerType}`;
449
+ return className ? `${base} ${className}` : base;
450
+ }
451
+ function setupContainerInitEffect(resolvedFields, eventBus, componentType, fieldKeyFn, injector) {
452
+ const allReady = computed(() => {
453
+ const fields = resolvedFields();
454
+ return fields.length > 0 && fields.every((field) => field.renderReady());
455
+ }, ...ngDevMode ? [{ debugName: "allReady" }] : /* istanbul ignore next */ []);
456
+ explicitEffect([allReady], ([ready]) => {
457
+ if (ready) emitComponentInitialized(eventBus, typeof componentType === "function" ? componentType() : componentType, fieldKeyFn(), injector);
458
+ });
459
+ }
460
+ const EMPTY_ERRORS = Object.freeze([]);
461
+ function snapshotParkedDomState(fieldTree) {
462
+ const state = fieldTree?.();
463
+ if (!state) return null;
464
+ return {
465
+ disabled: state.disabled?.() ?? false,
466
+ readonly: state.readonly?.() ?? false,
467
+ required: state.required?.() ?? false,
468
+ touched: state.touched?.() ?? false,
469
+ dirty: state.dirty?.() ?? false,
470
+ errors: state.errors?.() ?? EMPTY_ERRORS
471
+ };
472
+ }
473
+ function isSameParkedDomState(a, b) {
474
+ return a === b || a !== null && b !== null && a.disabled === b.disabled && a.readonly === b.readonly && a.required === b.required && a.touched === b.touched && a.dirty === b.dirty && a.errors === b.errors;
475
+ }
476
+ var DfFieldOutlet = class DfFieldOutlet {
477
+ dfFieldOutlet = input.required(...ngDevMode ? [{ debugName: "dfFieldOutlet" }] : /* istanbul ignore next */ []);
478
+ dfFieldOutletEnvironmentInjector = input(void 0, ...ngDevMode ? [{ debugName: "dfFieldOutletEnvironmentInjector" }] : /* istanbul ignore next */ []);
479
+ vcrRef = inject(ViewContainerRef);
480
+ vcr = signal(this.vcrRef).asReadonly();
481
+ destroyRef = inject(DestroyRef);
482
+ wrapperAutoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);
483
+ wrapperRegistry = inject(WRAPPER_REGISTRY);
484
+ defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
485
+ readonlyFieldCache = inject(READONLY_FIELD_TREE_CACHE);
486
+ eventBus = inject(EventBus, { optional: true });
487
+ injector = inject(Injector);
488
+ fieldComponent = new FieldComponentSlot();
489
+ componentIdentity = computed(() => this.dfFieldOutlet().component, ...ngDevMode ? [{ debugName: "componentIdentity" }] : /* istanbul ignore next */ []);
490
+ renderReady = computed(() => this.dfFieldOutlet().renderReady() && !this.dfFieldOutlet().hidden(), ...ngDevMode ? [{ debugName: "renderReady" }] : /* istanbul ignore next */ []);
491
+ hidden = computed(() => this.dfFieldOutlet().hidden(), ...ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []);
492
+ rawInputs = computed(() => this.dfFieldOutlet().inputs(), ...ngDevMode ? [{ debugName: "rawInputs" }] : /* istanbul ignore next */ []);
493
+ wrappers = computed(() => resolveWrappers(this.dfFieldOutlet().fieldDef, this.defaultWrappersSignal?.(), this.wrapperAutoAssociations, this.wrapperRegistry), {
494
+ ...ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {},
495
+ equal: isSameWrapperChain
496
+ });
497
+ fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type, this.dfFieldOutlet().fieldDef.validationMessages), ...ngDevMode ? [{ debugName: "fieldInputs" }] : /* istanbul ignore next */ []);
498
+ defaultEnvInjector = inject(EnvironmentInjector);
499
+ fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector, ...ngDevMode ? [{ debugName: "fieldEnvInjector" }] : /* istanbul ignore next */ []);
500
+ fieldInjector = computed(() => this.dfFieldOutlet().injector, ...ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []);
501
+ outermostHostClasses = computed(() => getGridClassString(this.dfFieldOutlet().fieldDef) || void 0, ...ngDevMode ? [{ debugName: "outermostHostClasses" }] : /* istanbul ignore next */ []);
502
+ formOptions = inject(FORM_OPTIONS, { optional: true });
503
+ globalFieldWindowing = inject(FIELD_WINDOWING);
504
+ viewportObserver = inject(FieldViewportObserver, { optional: true });
505
+ parking = computed(() => resolveFieldWindowing(this.globalFieldWindowing, this.formOptions?.()?.fieldWindowing).park, ...ngDevMode ? [{ debugName: "parking" }] : /* istanbul ignore next */ []);
506
+ observedElement = computed(() => {
507
+ const state = this.fieldComponent.snapshot();
508
+ return state.phase === "empty" ? null : state.ref.location.nativeElement;
509
+ }, ...ngDevMode ? [{ debugName: "observedElement" }] : /* istanbul ignore next */ []);
510
+ visibility = derivedFrom([this.observedElement, this.parking], pipe(switchMap(([element, parking]) => {
511
+ const observer = this.viewportObserver;
512
+ return parking.enabled && element && observer ? observer.observe(element, parking.margin) : of(true);
513
+ })), { initialValue: true });
514
+ focusEpoch = signal(0, ...ngDevMode ? [{ debugName: "focusEpoch" }] : /* istanbul ignore next */ []);
515
+ parked = computed(() => this.parking().enabled && !this.visibility(), ...ngDevMode ? [{ debugName: "parked" }] : /* istanbul ignore next */ []);
516
+ fieldTree = computed(() => {
517
+ const candidate = this.rawInputs()["field"];
518
+ return typeof candidate === "function" ? candidate : void 0;
519
+ }, {
520
+ ...ngDevMode ? { debugName: "fieldTree" } : /* istanbul ignore next */ {},
521
+ equal: Object.is
522
+ });
523
+ parkedDomState = computed(() => this.parked() ? snapshotParkedDomState(this.fieldTree()) : null, {
524
+ ...ngDevMode ? { debugName: "parkedDomState" } : /* istanbul ignore next */ {},
525
+ equal: isSameParkedDomState
526
+ });
527
+ constructor() {
528
+ createWrapperChainController({
529
+ vcr: this.vcr,
530
+ wrappers: this.wrappers,
531
+ gate: this.renderReady,
532
+ rebuildKey: this.componentIdentity,
533
+ fieldInputs: this.fieldInputs,
534
+ fieldInjector: this.fieldInjector,
535
+ outermostHostClasses: this.outermostHostClasses,
536
+ beforeRebuild: () => this.fieldComponent.detach(),
537
+ renderInnermost: (slot) => {
538
+ const resolved = this.dfFieldOutlet();
539
+ this.fieldComponent.mountOrReuse(slot, resolved.component, resolved.injector, this.fieldEnvInjector(), this.rawInputs(), this.fieldInputs());
540
+ }
541
+ });
542
+ explicitEffect([this.rawInputs, this.fieldInputs], ([rawInputs, fieldInputs]) => this.fieldComponent.pushInputs(rawInputs, fieldInputs));
543
+ explicitEffect([this.hidden], ([hidden]) => {
544
+ if (!hidden || !this.eventBus) return;
545
+ const resolved = this.dfFieldOutlet();
546
+ const groupContext = resolved.injector.get(GROUP_CONTEXT, null);
547
+ for (const { type, path } of collectInitializingContainers([resolved.fieldDef], groupContext?.groupPath())) emitComponentInitialized(this.eventBus, type, path, this.injector);
548
+ });
549
+ explicitEffect([this.observedElement, this.parking], ([element, parking], onCleanup) => {
550
+ if (!parking.enabled || !element) return;
551
+ const bumpEpoch = () => queueMicrotask(() => {
552
+ if (!this.destroyRef.destroyed) this.focusEpoch.update((n) => n + 1);
553
+ });
554
+ element.addEventListener("focusin", bumpEpoch);
555
+ element.addEventListener("focusout", bumpEpoch);
556
+ onCleanup(() => {
557
+ element.removeEventListener("focusin", bumpEpoch);
558
+ element.removeEventListener("focusout", bumpEpoch);
559
+ });
560
+ });
561
+ explicitEffect([
562
+ this.parked,
563
+ this.focusEpoch,
564
+ this.parkedDomState
565
+ ], ([parked, , domState]) => {
566
+ if (parked && this.canPark(domState)) {
567
+ const alreadyParked = this.fieldComponent.parked();
568
+ this.fieldComponent.park();
569
+ if (alreadyParked) this.fieldComponent.refresh();
570
+ } else this.fieldComponent.unpark();
571
+ });
572
+ this.destroyRef.onDestroy(() => this.fieldComponent.destroyOnTeardown());
573
+ }
574
+ canPark(domState) {
575
+ const element = this.observedElement();
576
+ if (!element) return false;
577
+ if (element.contains(document.activeElement)) return false;
578
+ return (domState?.errors.length ?? 0) === 0;
579
+ }
580
+ static ɵfac = i0.ɵɵngDeclareFactory({
581
+ minVersion: "12.0.0",
582
+ version: "22.2.5",
583
+ ngImport: i0,
584
+ type: DfFieldOutlet,
585
+ deps: [],
586
+ target: i0.ɵɵFactoryTarget.Directive
587
+ });
588
+ static ɵdir = i0.ɵɵngDeclareDirective({
589
+ minVersion: "17.1.0",
590
+ version: "22.2.5",
591
+ type: DfFieldOutlet,
592
+ isStandalone: true,
593
+ selector: "[dfFieldOutlet]",
594
+ inputs: {
595
+ dfFieldOutlet: {
596
+ classPropertyName: "dfFieldOutlet",
597
+ publicName: "dfFieldOutlet",
598
+ isSignal: true,
599
+ isRequired: true,
600
+ transformFunction: null
601
+ },
602
+ dfFieldOutletEnvironmentInjector: {
603
+ classPropertyName: "dfFieldOutletEnvironmentInjector",
604
+ publicName: "dfFieldOutletEnvironmentInjector",
605
+ isSignal: true,
606
+ isRequired: false,
607
+ transformFunction: null
608
+ }
609
+ },
610
+ ngImport: i0
611
+ });
612
+ };
613
+ i0.ɵɵngDeclareClassMetadata({
614
+ minVersion: "12.0.0",
615
+ version: "22.2.5",
616
+ ngImport: i0,
617
+ type: DfFieldOutlet,
618
+ decorators: [{
619
+ type: Directive,
620
+ args: [{ selector: "[dfFieldOutlet]" }]
621
+ }],
622
+ ctorParameters: () => [],
623
+ propDecorators: {
624
+ dfFieldOutlet: [{
625
+ type: i0.Input,
626
+ args: [{
627
+ isSignal: true,
628
+ alias: "dfFieldOutlet",
629
+ required: true
630
+ }]
631
+ }],
632
+ dfFieldOutletEnvironmentInjector: [{
633
+ type: i0.Input,
634
+ args: [{
635
+ isSignal: true,
636
+ alias: "dfFieldOutletEnvironmentInjector",
637
+ required: false
638
+ }]
639
+ }]
640
+ }
641
+ });
642
+ export { initializationComponentPath as a, createWrapperChainController as c, FieldViewportObserver as d, resolveFieldParking as f, FIELD_WINDOWING as h, initializationComponentKey as i, isSameWrapperChain as l, clampWindowSize as m, collectInitializingContainerKeys as n, setupContainerInitEffect as o, resolveFieldWindowing as p, computeContainerHostClasses as r, emitComponentInitialized as s, DfFieldOutlet as t, resolveWrappers as u };
643
+
644
+ //# sourceMappingURL=ng-forge-dynamic-forms-df-field-outlet.directive-CQsDMV-s.mjs.map