@ng-forge/dynamic-forms 1.1.0 → 1.2.0-next.10
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.
- package/README.md +11 -11
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-CvjDqYyq.mjs → ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs} +9 -13
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CcoUnroD.mjs → ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs} +29 -7
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs +1054 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-DZfVWjiM.mjs → ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs} +14 -17
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-D_yRuLpu.mjs → ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs} +5 -5
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs +17 -0
- package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs +25 -0
- package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs +24 -0
- package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-CPFgu3pM.mjs → ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs} +9 -13
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +49 -107
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +434 -139
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs} +1930 -1847
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs → ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs} +94 -15
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-BBWmqQA_.mjs → ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs} +5 -5
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs +570 -0
- package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C-f80sbk.mjs → ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs} +4 -4
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C-f80sbk.mjs.map → ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +2 -2
- package/package.json +2 -2
- package/types/ng-forge-dynamic-forms-integration.d.ts +15 -33
- package/types/ng-forge-dynamic-forms-internal.d.ts +705 -292
- package/types/ng-forge-dynamic-forms.d.ts +400 -46
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-CvjDqYyq.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CcoUnroD.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-DZfVWjiM.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs +0 -24
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-D_yRuLpu.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs +0 -17
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-CPFgu3pM.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BBWmqQA_.mjs.map +0 -1
|
@@ -1,15 +1,135 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { InjectionToken, inject, PLATFORM_ID, DestroyRef, Injectable, reflectComponentType, Injector, EnvironmentInjector, computed, signal, afterNextRender, input, ViewContainerRef, Directive, isSignal, runInInjectionContext, untracked, linkedSignal, injectAsync, resource, ChangeDetectionStrategy, Component, PendingTasks, ElementRef, isDevMode, TemplateRef, model, contentChildren, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { submit, required, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, email, minLength, maxLength, pattern, min, max, schema, validateStandardSchema, validateTree, form, provideSignalFormsConfig } from '@angular/forms/signals';
|
|
8
|
-
import { isStandardSchemaMarker } from '@ng-forge/dynamic-forms/schema';
|
|
4
|
+
import { derivedFrom } from 'ngxtension/derived-from';
|
|
5
|
+
import { of, Observable, distinctUntilChanged, forkJoin, from, catchError, map, switchMap, pipe, isObservable, firstValueFrom, exhaustMap, EMPTY, defer, tap, scan, Subject, concatMap, filter, take, shareReplay } from 'rxjs';
|
|
6
|
+
import { DynamicFormLogger, WRAPPER_COMPONENT_LOAD_CACHE, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, ComponentInitializedEvent, isPageField, isGroupField, isArrayField, isRowField, isGenericContainerField, hasChildFields, normalizeFieldsArray, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, getGridClassString, FORM_OPTIONS, GROUP_CONTEXT, FIELD_REGISTRY, DynamicFormError, isContainerField, isSimplifiedArrayField, getFieldValueHandling, keyBy, mapValues, isEqual, isDerivationLogicConfig, hasTargetProperty, ARRAY_CONTEXT, FORM_ID_PREFIX, baseFieldMapper, detectFormMode, validatePageNesting, assertNever, FunctionRegistryService, ADDON_TYPE_REGISTRY, deepMergeDefaults, FIELD_SIGNAL_CONTEXT, derivedFromDeferred, FieldContextRegistryService, evaluateCondition, createWarningTracker, RootFormRegistryService, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, EXTERNAL_DATA, DEPRECATION_WARNING_TRACKER, createAddonActionRegistry, ADDON_ACTION_REGISTRY, HttpConditionCache, HTTP_CONDITION_CACHE, HttpConditionFunctionCacheService, AsyncConditionFunctionCacheService, DynamicValueFunctionCacheService, LogicFunctionCacheService, DF_FIELD_TEMPLATES, rowFieldMapper, groupFieldMapper, arrayFieldMapper, pageFieldMapper, textFieldMapper, containerFieldMapper, isWrapperTypeDefinition, ADDON_TYPE_COMPONENT_CACHE, ADDON_TYPE_DEFINITIONS, createDefaultDerivationLogConfig, NoopLogger, EMIT_FORM_VALUE_ON_EVENTS, DynamicTextPipe, ADDON_ACTION_HANDLERS } from '@ng-forge/dynamic-forms/internal';
|
|
9
7
|
import { isPlatformBrowser, AsyncPipe, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';
|
|
8
|
+
import { takeUntilDestroyed, toObservable, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
9
|
+
import { submit, form, provideSignalFormsConfig } from '@angular/forms/signals';
|
|
10
10
|
import { NG_STATUS_CLASSES } from '@angular/forms/signals/compat';
|
|
11
11
|
|
|
12
|
+
const FIELD_WINDOWING = new InjectionToken('FIELD_WINDOWING', {
|
|
13
|
+
providedIn: 'root',
|
|
14
|
+
factory: () => ({ enabled: false, eager: 12, placeholderHeight: '4rem', park: { enabled: false, margin: '100%' } }),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function clampWindowSize(value, fallback) {
|
|
18
|
+
if (value === undefined || !Number.isFinite(value))
|
|
19
|
+
return fallback;
|
|
20
|
+
return Math.max(0, Math.floor(value));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DEFAULT_MARGIN = '100%';
|
|
24
|
+
const ROOT_MARGIN_PART = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?(?:px|%)$/i;
|
|
25
|
+
function normalizeFieldParkingMargin(value, fallback = DEFAULT_MARGIN) {
|
|
26
|
+
const normalizedFallback = isValidRootMargin(fallback) ? fallback.trim() : DEFAULT_MARGIN;
|
|
27
|
+
if (!value || !isValidRootMargin(value))
|
|
28
|
+
return normalizedFallback;
|
|
29
|
+
return value.trim().split(/\s+/).join(' ');
|
|
30
|
+
}
|
|
31
|
+
function isValidRootMargin(value) {
|
|
32
|
+
const parts = value.trim().split(/\s+/);
|
|
33
|
+
return parts.length >= 1 && parts.length <= 4 && parts.every((part) => ROOT_MARGIN_PART.test(part));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveFieldWindowing(global, perForm) {
|
|
37
|
+
if (perForm === undefined) {
|
|
38
|
+
return global;
|
|
39
|
+
}
|
|
40
|
+
if (typeof perForm === 'boolean') {
|
|
41
|
+
return { ...global, enabled: perForm };
|
|
42
|
+
}
|
|
43
|
+
const configuresMounting = 'eager' in perForm || 'placeholderHeight' in perForm || Object.keys(perForm).length === 0;
|
|
44
|
+
return {
|
|
45
|
+
enabled: configuresMounting ? true : global.enabled,
|
|
46
|
+
eager: clampWindowSize(perForm.eager, global.eager),
|
|
47
|
+
placeholderHeight: perForm.placeholderHeight ?? global.placeholderHeight,
|
|
48
|
+
park: resolveFieldParking(global.park, perForm.park),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function resolveFieldParking(global, perForm) {
|
|
52
|
+
if (perForm === undefined)
|
|
53
|
+
return global;
|
|
54
|
+
if (typeof perForm === 'boolean')
|
|
55
|
+
return { ...global, enabled: perForm };
|
|
56
|
+
return { enabled: true, margin: normalizeFieldParkingMargin(perForm.margin, global.margin) };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const ALWAYS_VISIBLE = of(true);
|
|
60
|
+
class FieldViewportObserver {
|
|
61
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID)) && typeof IntersectionObserver !== 'undefined';
|
|
62
|
+
observers = new Map();
|
|
63
|
+
constructor() {
|
|
64
|
+
inject(DestroyRef).onDestroy(() => {
|
|
65
|
+
for (const { observer } of this.observers.values())
|
|
66
|
+
observer.disconnect();
|
|
67
|
+
this.observers.clear();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
observe(el, rootMargin) {
|
|
71
|
+
if (!this.isBrowser)
|
|
72
|
+
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, new Set([listener]));
|
|
89
|
+
entry.observer.observe(el);
|
|
90
|
+
}
|
|
91
|
+
stopObserving(el, rootMargin, listener) {
|
|
92
|
+
const entry = this.observers.get(rootMargin);
|
|
93
|
+
if (!entry)
|
|
94
|
+
return;
|
|
95
|
+
const listeners = entry.listeners.get(el);
|
|
96
|
+
if (!listeners)
|
|
97
|
+
return;
|
|
98
|
+
listeners.delete(listener);
|
|
99
|
+
if (listeners.size > 0)
|
|
100
|
+
return;
|
|
101
|
+
entry.observer.unobserve(el);
|
|
102
|
+
entry.listeners.delete(el);
|
|
103
|
+
if (entry.listeners.size > 0)
|
|
104
|
+
return;
|
|
105
|
+
entry.observer.disconnect();
|
|
106
|
+
this.observers.delete(rootMargin);
|
|
107
|
+
}
|
|
108
|
+
observerFor(rootMargin) {
|
|
109
|
+
const existing = this.observers.get(rootMargin);
|
|
110
|
+
if (existing)
|
|
111
|
+
return existing;
|
|
112
|
+
const listeners = new Map();
|
|
113
|
+
const observer = new IntersectionObserver((entries) => {
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
for (const listener of listeners.get(entry.target) ?? [])
|
|
116
|
+
listener(entry.isIntersecting);
|
|
117
|
+
}
|
|
118
|
+
}, { rootMargin });
|
|
119
|
+
const entry = { observer, listeners };
|
|
120
|
+
this.observers.set(rootMargin, entry);
|
|
121
|
+
return entry;
|
|
122
|
+
}
|
|
123
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FieldViewportObserver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
124
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FieldViewportObserver });
|
|
125
|
+
}
|
|
126
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FieldViewportObserver, decorators: [{
|
|
127
|
+
type: Injectable
|
|
128
|
+
}], ctorParameters: () => [] });
|
|
129
|
+
|
|
12
130
|
const EMPTY_WRAPPERS = Object.freeze([]);
|
|
131
|
+
const FIELD_ERRORS_WRAPPER = 'field-errors';
|
|
132
|
+
const FIELD_ERRORS_CONFIG = Object.freeze({ type: FIELD_ERRORS_WRAPPER });
|
|
13
133
|
function isSameWrapperChain(a, b) {
|
|
14
134
|
if (a === b)
|
|
15
135
|
return true;
|
|
@@ -21,18 +141,29 @@ function isSameWrapperChain(a, b) {
|
|
|
21
141
|
}
|
|
22
142
|
return true;
|
|
23
143
|
}
|
|
24
|
-
function resolveWrappers(field, defaultWrappers, autoAssociations) {
|
|
25
|
-
|
|
144
|
+
function resolveWrappers(field, defaultWrappers, autoAssociations, wrapperRegistry) {
|
|
145
|
+
if (field.wrappers === null) {
|
|
26
146
|
return EMPTY_WRAPPERS;
|
|
27
147
|
}
|
|
28
148
|
const autoWrappers = field.skipAutoWrappers ? EMPTY_WRAPPERS : (autoAssociations.get(field.type) ?? EMPTY_WRAPPERS);
|
|
29
149
|
const defaults = field.skipDefaultWrappers ? EMPTY_WRAPPERS : (defaultWrappers ?? EMPTY_WRAPPERS);
|
|
30
150
|
const fieldLevel = field.wrappers ?? EMPTY_WRAPPERS;
|
|
31
|
-
|
|
32
|
-
|
|
151
|
+
const composed = [...autoWrappers, ...defaults, ...fieldLevel];
|
|
152
|
+
if (needsErrorWrapper(field, composed, wrapperRegistry)) {
|
|
153
|
+
composed.push(FIELD_ERRORS_CONFIG);
|
|
33
154
|
}
|
|
34
|
-
return
|
|
155
|
+
return composed.length === 0 ? EMPTY_WRAPPERS : composed;
|
|
35
156
|
}
|
|
157
|
+
const VALIDATABLE_CONTAINERS = new Set(['group', 'array']);
|
|
158
|
+
function needsErrorWrapper(field, composed, wrapperRegistry) {
|
|
159
|
+
if (!VALIDATABLE_CONTAINERS.has(field.type))
|
|
160
|
+
return false;
|
|
161
|
+
if (!field.validators || field.validators.length === 0)
|
|
162
|
+
return false;
|
|
163
|
+
return !composed.some((w) => w.type === FIELD_ERRORS_WRAPPER || wrapperRegistry.get(w.type)?.rendersFieldErrors);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
|
|
36
167
|
|
|
37
168
|
const inputNamesCache = new WeakMap();
|
|
38
169
|
function getDeclaredInputs(componentType) {
|
|
@@ -55,24 +186,33 @@ function hasDefaultExport(value) {
|
|
|
55
186
|
function resolveDefaultExport(result) {
|
|
56
187
|
return hasDefaultExport(result) ? result.default : result;
|
|
57
188
|
}
|
|
58
|
-
async function loadWrapperComponent(type, registry, cache) {
|
|
189
|
+
async function loadWrapperComponent(type, registry, cache, loadCache) {
|
|
59
190
|
const cached = cache.get(type);
|
|
60
191
|
if (cached)
|
|
61
192
|
return cached;
|
|
193
|
+
const pending = loadCache?.get(type);
|
|
194
|
+
if (pending)
|
|
195
|
+
return pending;
|
|
62
196
|
const definition = registry.get(type);
|
|
63
197
|
if (!definition)
|
|
64
198
|
return undefined;
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
199
|
+
const load = (async () => {
|
|
200
|
+
const component = resolveDefaultExport(await definition.loadComponent());
|
|
201
|
+
if (component)
|
|
202
|
+
cache.set(type, component);
|
|
203
|
+
return component;
|
|
204
|
+
})().finally(() => loadCache?.delete(type));
|
|
205
|
+
loadCache?.set(type, load);
|
|
206
|
+
return load;
|
|
207
|
+
}
|
|
208
|
+
function loadWrapperComponents(configs, registry, cache, logger, loadCache) {
|
|
71
209
|
if (configs.length === 0)
|
|
72
210
|
return of([]);
|
|
73
|
-
return forkJoin(configs.map((config) => from(loadWrapperComponent(config.type, registry, cache)).pipe(catchError(() => of(undefined)), map((component) => {
|
|
211
|
+
return forkJoin(configs.map((config) => from(loadWrapperComponent(config.type, registry, cache, loadCache)).pipe(catchError(() => of(undefined)), map((component) => {
|
|
74
212
|
if (!component) {
|
|
75
|
-
|
|
213
|
+
if (DEV_MODE) {
|
|
214
|
+
logger.error(`Wrapper type '${config.type}' could not be loaded. Ensure it is registered via provideDynamicForm().`);
|
|
215
|
+
}
|
|
76
216
|
return null;
|
|
77
217
|
}
|
|
78
218
|
return { config, component };
|
|
@@ -112,9 +252,11 @@ function renderStep(slot, remaining, options, refs) {
|
|
|
112
252
|
ref.changeDetectorRef.detectChanges();
|
|
113
253
|
const inner = resolveInnerSlot(ref);
|
|
114
254
|
if (!inner) {
|
|
115
|
-
|
|
116
|
-
`
|
|
117
|
-
|
|
255
|
+
if (DEV_MODE) {
|
|
256
|
+
options.logger.error(`Wrapper component for type '${wrapper.config.type}' does not provide a 'fieldComponent' ViewContainerRef. ` +
|
|
257
|
+
`Ensure the wrapper component has a viewChild('fieldComponent', { read: ViewContainerRef }) query ` +
|
|
258
|
+
`and that #fieldComponent is not inside a conditional (@if, @defer).`);
|
|
259
|
+
}
|
|
118
260
|
while (refs.length)
|
|
119
261
|
refs.pop().destroy();
|
|
120
262
|
return;
|
|
@@ -156,27 +298,34 @@ function createWrapperChainController(opts) {
|
|
|
156
298
|
const deps = injectChainDeps();
|
|
157
299
|
const state = createStateSignal(opts);
|
|
158
300
|
const mounted = { value: null };
|
|
301
|
+
const detached = { value: null };
|
|
159
302
|
let refs = [];
|
|
160
303
|
buildEmissionStream(state, deps)
|
|
161
304
|
.pipe(takeUntilDestroyed(deps.destroyRef))
|
|
162
305
|
.subscribe((emission) => {
|
|
163
306
|
try {
|
|
164
|
-
refs = applyEmission(emission, { opts, deps, mounted, refs });
|
|
307
|
+
refs = applyEmission(emission, { opts, deps, mounted, refs, detached });
|
|
165
308
|
}
|
|
166
309
|
catch (err) {
|
|
167
310
|
deps.logger.error('Wrapper chain render failed; tearing down partial state.', err);
|
|
168
311
|
opts.vcr().clear();
|
|
169
312
|
refs = [];
|
|
170
313
|
mounted.value = null;
|
|
314
|
+
detached.value?.destroy();
|
|
315
|
+
detached.value = null;
|
|
171
316
|
}
|
|
172
317
|
});
|
|
173
318
|
pushFieldInputsOnChange(opts, () => refs);
|
|
174
|
-
deps.destroyRef.onDestroy(() =>
|
|
319
|
+
deps.destroyRef.onDestroy(() => {
|
|
320
|
+
detached.value?.destroy();
|
|
321
|
+
opts.vcr().clear();
|
|
322
|
+
});
|
|
175
323
|
}
|
|
176
324
|
function injectChainDeps() {
|
|
177
325
|
return {
|
|
178
326
|
registry: inject(WRAPPER_REGISTRY),
|
|
179
327
|
cache: inject(WRAPPER_COMPONENT_CACHE),
|
|
328
|
+
loadCache: inject(WRAPPER_COMPONENT_LOAD_CACHE),
|
|
180
329
|
logger: inject(DynamicFormLogger),
|
|
181
330
|
destroyRef: inject(DestroyRef),
|
|
182
331
|
environmentInjector: inject(EnvironmentInjector),
|
|
@@ -200,16 +349,27 @@ function resolveLoadedWrappers(state, deps) {
|
|
|
200
349
|
const loaded = state.wrappers.map((config) => ({ config, component: deps.cache.get(config.type) }));
|
|
201
350
|
return of({ state, loaded });
|
|
202
351
|
}
|
|
203
|
-
return loadWrapperComponents(state.wrappers, deps.registry, deps.cache, deps.logger).pipe(map((loaded) => ({ state, loaded })));
|
|
352
|
+
return loadWrapperComponents(state.wrappers, deps.registry, deps.cache, deps.logger, deps.loadCache).pipe(map((loaded) => ({ state, loaded })));
|
|
204
353
|
}
|
|
205
354
|
function applyEmission({ state, loaded }, ctx) {
|
|
206
355
|
const { opts, deps, mounted, refs } = ctx;
|
|
207
356
|
const vcr = opts.vcr();
|
|
208
357
|
const structurallyChanged = isStructurallyDifferent(mounted.value, state);
|
|
209
|
-
if (!structurallyChanged && !state.open)
|
|
358
|
+
if (!structurallyChanged && !state.open) {
|
|
359
|
+
if (vcr.length > 0)
|
|
360
|
+
ctx.detached.value = vcr.detach(0) ?? ctx.detached.value;
|
|
361
|
+
return refs;
|
|
362
|
+
}
|
|
363
|
+
if (state.open && !structurallyChanged && mounted.value !== null && ctx.detached.value) {
|
|
364
|
+
vcr.insert(ctx.detached.value);
|
|
365
|
+
ctx.detached.value = null;
|
|
210
366
|
return refs;
|
|
367
|
+
}
|
|
211
368
|
if (structurallyChanged && mounted.value !== null) {
|
|
212
|
-
|
|
369
|
+
const preservesDetachedOuterView = opts.beforeRebuild?.() === true;
|
|
370
|
+
if (ctx.detached.value && !preservesDetachedOuterView)
|
|
371
|
+
ctx.detached.value.destroy();
|
|
372
|
+
ctx.detached.value = null;
|
|
213
373
|
vcr.clear();
|
|
214
374
|
mounted.value = null;
|
|
215
375
|
}
|
|
@@ -246,7 +406,7 @@ function pushFieldInputsOnChange(opts, getRefs) {
|
|
|
246
406
|
});
|
|
247
407
|
}
|
|
248
408
|
|
|
249
|
-
function buildFieldInputs(rawInputs, cache, fieldType) {
|
|
409
|
+
function buildFieldInputs(rawInputs, cache, fieldType, validationMessages) {
|
|
250
410
|
const fieldTreeCandidate = rawInputs['field'];
|
|
251
411
|
const hasFieldTree = fieldTreeCandidate !== undefined && typeof fieldTreeCandidate === 'function';
|
|
252
412
|
const readonlyField = hasFieldTree ? toReadonlyFieldTreeCached(cache, fieldTreeCandidate) : undefined;
|
|
@@ -258,6 +418,7 @@ function buildFieldInputs(rawInputs, cache, fieldType) {
|
|
|
258
418
|
type: fieldType,
|
|
259
419
|
field: readonlyField,
|
|
260
420
|
setValue,
|
|
421
|
+
...(validationMessages !== undefined && { validationMessages }),
|
|
261
422
|
};
|
|
262
423
|
}
|
|
263
424
|
|
|
@@ -266,14 +427,19 @@ class FieldComponentSlot {
|
|
|
266
427
|
state = signal(EMPTY_STATE, /* @ts-ignore */
|
|
267
428
|
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
268
429
|
lastInputs = null;
|
|
430
|
+
_parked = signal(false, /* @ts-ignore */
|
|
431
|
+
...(ngDevMode ? [{ debugName: "_parked" }] : /* istanbul ignore next */ []));
|
|
269
432
|
phase = () => this.state().phase;
|
|
270
433
|
snapshot = () => this.state();
|
|
434
|
+
parked = this._parked.asReadonly();
|
|
271
435
|
mountOrReuse(slot, component, fieldInjector, environmentInjector, rawInputs, fieldInputs) {
|
|
272
436
|
const current = this.state();
|
|
273
437
|
if (current.phase !== 'empty' && current.ref.componentType === component) {
|
|
274
438
|
slot.insert(current.ref.hostView);
|
|
275
439
|
this.lastInputs = null;
|
|
276
440
|
this.state.set(Object.freeze({ phase: 'mounted', ref: current.ref, slot }));
|
|
441
|
+
if (this._parked())
|
|
442
|
+
current.ref.changeDetectorRef.detach();
|
|
277
443
|
this.pushInputs(rawInputs, fieldInputs);
|
|
278
444
|
if (current.phase === 'detached')
|
|
279
445
|
this.replayFocus(current.focusSnapshot);
|
|
@@ -282,6 +448,7 @@ class FieldComponentSlot {
|
|
|
282
448
|
if (current.phase !== 'empty')
|
|
283
449
|
current.ref.destroy();
|
|
284
450
|
this.lastInputs = null;
|
|
451
|
+
this._parked.set(false);
|
|
285
452
|
this.state.set(EMPTY_STATE);
|
|
286
453
|
const ref = slot.createComponent(component, {
|
|
287
454
|
environmentInjector,
|
|
@@ -310,19 +477,42 @@ class FieldComponentSlot {
|
|
|
310
477
|
detach() {
|
|
311
478
|
const current = this.state();
|
|
312
479
|
if (current.phase !== 'mounted')
|
|
313
|
-
return;
|
|
480
|
+
return false;
|
|
314
481
|
const focusSnapshot = this.captureFocus(current.ref);
|
|
315
482
|
const idx = current.slot.indexOf(current.ref.hostView);
|
|
316
483
|
if (idx >= 0)
|
|
317
484
|
current.slot.detach(idx);
|
|
318
485
|
this.lastInputs = null;
|
|
319
486
|
this.state.set(Object.freeze({ phase: 'detached', ref: current.ref, focusSnapshot }));
|
|
487
|
+
return idx < 0;
|
|
488
|
+
}
|
|
489
|
+
park() {
|
|
490
|
+
const current = this.state();
|
|
491
|
+
if (current.phase === 'empty' || this._parked())
|
|
492
|
+
return;
|
|
493
|
+
current.ref.changeDetectorRef.detach();
|
|
494
|
+
this._parked.set(true);
|
|
495
|
+
}
|
|
496
|
+
refresh() {
|
|
497
|
+
const current = this.state();
|
|
498
|
+
if (current.phase === 'empty' || !this._parked())
|
|
499
|
+
return;
|
|
500
|
+
current.ref.changeDetectorRef.detectChanges();
|
|
501
|
+
}
|
|
502
|
+
unpark() {
|
|
503
|
+
const current = this.state();
|
|
504
|
+
if (current.phase === 'empty' || !this._parked())
|
|
505
|
+
return;
|
|
506
|
+
current.ref.changeDetectorRef.reattach();
|
|
507
|
+
current.ref.changeDetectorRef.markForCheck();
|
|
508
|
+
this._parked.set(false);
|
|
320
509
|
}
|
|
321
510
|
destroyOnTeardown() {
|
|
322
511
|
const current = this.state();
|
|
323
512
|
if (current.phase === 'detached')
|
|
324
513
|
current.ref.destroy();
|
|
325
514
|
this.lastInputs = null;
|
|
515
|
+
this._parked.set(false);
|
|
326
516
|
this.state.set(EMPTY_STATE);
|
|
327
517
|
}
|
|
328
518
|
captureFocus(ref) {
|
|
@@ -347,6 +537,100 @@ class FieldComponentSlot {
|
|
|
347
537
|
}
|
|
348
538
|
}
|
|
349
539
|
|
|
540
|
+
function emitComponentInitialized(eventBus, componentType, componentKey, injector) {
|
|
541
|
+
const logger = injector.get(DynamicFormLogger);
|
|
542
|
+
afterNextRender(() => {
|
|
543
|
+
try {
|
|
544
|
+
eventBus.dispatch(ComponentInitializedEvent, componentType, componentKey);
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
logger.error(`Failed to emit initialization event for ${componentType} '${componentKey}'`, error);
|
|
548
|
+
}
|
|
549
|
+
}, { injector });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function initializationComponentKey(type, key) {
|
|
553
|
+
return `${type}:${key}`;
|
|
554
|
+
}
|
|
555
|
+
function initializationComponentPath(key, groupPath) {
|
|
556
|
+
return groupPath ? `${groupPath}.${key}` : key;
|
|
557
|
+
}
|
|
558
|
+
function initializationComponentType(field) {
|
|
559
|
+
if (isPageField(field))
|
|
560
|
+
return 'page';
|
|
561
|
+
if (isGroupField(field))
|
|
562
|
+
return 'group';
|
|
563
|
+
if (isArrayField(field))
|
|
564
|
+
return 'array';
|
|
565
|
+
if (isRowField(field) || isGenericContainerField(field))
|
|
566
|
+
return 'container';
|
|
567
|
+
return undefined;
|
|
568
|
+
}
|
|
569
|
+
function collectInitializingContainers(fields, groupPath = '') {
|
|
570
|
+
const identities = [];
|
|
571
|
+
for (const field of fields) {
|
|
572
|
+
if (field.hidden === true)
|
|
573
|
+
continue;
|
|
574
|
+
const type = initializationComponentType(field);
|
|
575
|
+
const componentPath = initializationComponentPath(field.key, groupPath);
|
|
576
|
+
if (type && field.key)
|
|
577
|
+
identities.push({ type, path: componentPath });
|
|
578
|
+
if (!hasChildFields(field))
|
|
579
|
+
continue;
|
|
580
|
+
const childGroupPath = isGroupField(field) ? componentPath : groupPath;
|
|
581
|
+
for (const child of normalizeFieldsArray(field.fields)) {
|
|
582
|
+
identities.push(...collectInitializingContainers((Array.isArray(child) ? child : [child]), childGroupPath));
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return identities;
|
|
586
|
+
}
|
|
587
|
+
function collectInitializingContainerKeys(fields, groupPath = '') {
|
|
588
|
+
return collectInitializingContainers(fields, groupPath).map(({ type, path }) => initializationComponentKey(type, path));
|
|
589
|
+
}
|
|
590
|
+
function computeContainerHostClasses(containerType, className) {
|
|
591
|
+
const base = `df-field df-${containerType}`;
|
|
592
|
+
return className ? `${base} ${className}` : base;
|
|
593
|
+
}
|
|
594
|
+
function setupContainerInitEffect(resolvedFields, eventBus, componentType, fieldKeyFn, injector) {
|
|
595
|
+
const allReady = computed(() => {
|
|
596
|
+
const fields = resolvedFields();
|
|
597
|
+
return fields.length > 0 && fields.every((field) => field.renderReady());
|
|
598
|
+
}, /* @ts-ignore */
|
|
599
|
+
...(ngDevMode ? [{ debugName: "allReady" }] : /* istanbul ignore next */ []));
|
|
600
|
+
explicitEffect([allReady], ([ready]) => {
|
|
601
|
+
if (ready) {
|
|
602
|
+
const type = typeof componentType === 'function' ? componentType() : componentType;
|
|
603
|
+
emitComponentInitialized(eventBus, type, fieldKeyFn(), injector);
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const EMPTY_ERRORS = Object.freeze([]);
|
|
609
|
+
function snapshotParkedDomState(fieldTree) {
|
|
610
|
+
const state = fieldTree?.();
|
|
611
|
+
if (!state)
|
|
612
|
+
return null;
|
|
613
|
+
return {
|
|
614
|
+
disabled: state.disabled?.() ?? false,
|
|
615
|
+
readonly: state.readonly?.() ?? false,
|
|
616
|
+
required: state.required?.() ?? false,
|
|
617
|
+
touched: state.touched?.() ?? false,
|
|
618
|
+
dirty: state.dirty?.() ?? false,
|
|
619
|
+
errors: state.errors?.() ?? EMPTY_ERRORS,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function isSameParkedDomState(a, b) {
|
|
623
|
+
return (a === b ||
|
|
624
|
+
(a !== null &&
|
|
625
|
+
b !== null &&
|
|
626
|
+
a.disabled === b.disabled &&
|
|
627
|
+
a.readonly === b.readonly &&
|
|
628
|
+
a.required === b.required &&
|
|
629
|
+
a.touched === b.touched &&
|
|
630
|
+
a.dirty === b.dirty &&
|
|
631
|
+
a.errors === b.errors));
|
|
632
|
+
}
|
|
633
|
+
|
|
350
634
|
class DfFieldOutlet {
|
|
351
635
|
dfFieldOutlet = input.required(/* @ts-ignore */
|
|
352
636
|
...(ngDevMode ? [{ debugName: "dfFieldOutlet" }] : /* istanbul ignore next */ []));
|
|
@@ -356,25 +640,53 @@ class DfFieldOutlet {
|
|
|
356
640
|
vcr = signal(this.vcrRef).asReadonly();
|
|
357
641
|
destroyRef = inject(DestroyRef);
|
|
358
642
|
wrapperAutoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);
|
|
643
|
+
wrapperRegistry = inject(WRAPPER_REGISTRY);
|
|
359
644
|
defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
|
|
360
645
|
readonlyFieldCache = inject(READONLY_FIELD_TREE_CACHE);
|
|
361
|
-
|
|
646
|
+
eventBus = inject(EventBus, { optional: true });
|
|
647
|
+
injector = inject(Injector);
|
|
648
|
+
fieldComponent = new FieldComponentSlot();
|
|
362
649
|
componentIdentity = computed(() => this.dfFieldOutlet().component, /* @ts-ignore */
|
|
363
650
|
...(ngDevMode ? [{ debugName: "componentIdentity" }] : /* istanbul ignore next */ []));
|
|
364
651
|
renderReady = computed(() => this.dfFieldOutlet().renderReady() && !this.dfFieldOutlet().hidden(), /* @ts-ignore */
|
|
365
652
|
...(ngDevMode ? [{ debugName: "renderReady" }] : /* istanbul ignore next */ []));
|
|
653
|
+
hidden = computed(() => this.dfFieldOutlet().hidden(), /* @ts-ignore */
|
|
654
|
+
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
366
655
|
rawInputs = computed(() => this.dfFieldOutlet().inputs(), /* @ts-ignore */
|
|
367
656
|
...(ngDevMode ? [{ debugName: "rawInputs" }] : /* istanbul ignore next */ []));
|
|
368
|
-
wrappers = computed(() => resolveWrappers(this.dfFieldOutlet().fieldDef, this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
|
|
369
|
-
fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type), /* @ts-ignore */
|
|
657
|
+
wrappers = computed(() => resolveWrappers(this.dfFieldOutlet().fieldDef, this.defaultWrappersSignal?.(), this.wrapperAutoAssociations, this.wrapperRegistry), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
|
|
658
|
+
fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type, this.dfFieldOutlet().fieldDef.validationMessages), /* @ts-ignore */
|
|
370
659
|
...(ngDevMode ? [{ debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
371
660
|
defaultEnvInjector = inject(EnvironmentInjector);
|
|
372
|
-
|
|
661
|
+
fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector, /* @ts-ignore */
|
|
373
662
|
...(ngDevMode ? [{ debugName: "fieldEnvInjector" }] : /* istanbul ignore next */ []));
|
|
374
|
-
|
|
663
|
+
fieldInjector = computed(() => this.dfFieldOutlet().injector, /* @ts-ignore */
|
|
375
664
|
...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
|
|
376
665
|
outermostHostClasses = computed(() => getGridClassString(this.dfFieldOutlet().fieldDef) || undefined, /* @ts-ignore */
|
|
377
666
|
...(ngDevMode ? [{ debugName: "outermostHostClasses" }] : /* istanbul ignore next */ []));
|
|
667
|
+
formOptions = inject(FORM_OPTIONS, { optional: true });
|
|
668
|
+
globalFieldWindowing = inject(FIELD_WINDOWING);
|
|
669
|
+
viewportObserver = inject(FieldViewportObserver, { optional: true });
|
|
670
|
+
parking = computed(() => resolveFieldWindowing(this.globalFieldWindowing, this.formOptions?.()?.fieldWindowing).park, /* @ts-ignore */
|
|
671
|
+
...(ngDevMode ? [{ debugName: "parking" }] : /* istanbul ignore next */ []));
|
|
672
|
+
observedElement = computed(() => {
|
|
673
|
+
const state = this.fieldComponent.snapshot();
|
|
674
|
+
return state.phase === 'empty' ? null : state.ref.location.nativeElement;
|
|
675
|
+
}, /* @ts-ignore */
|
|
676
|
+
...(ngDevMode ? [{ debugName: "observedElement" }] : /* istanbul ignore next */ []));
|
|
677
|
+
visibility = derivedFrom([this.observedElement, this.parking], pipe(switchMap(([element, parking]) => {
|
|
678
|
+
const observer = this.viewportObserver;
|
|
679
|
+
return parking.enabled && element && observer ? observer.observe(element, parking.margin) : of(true);
|
|
680
|
+
})), { initialValue: true });
|
|
681
|
+
focusEpoch = signal(0, /* @ts-ignore */
|
|
682
|
+
...(ngDevMode ? [{ debugName: "focusEpoch" }] : /* istanbul ignore next */ []));
|
|
683
|
+
parked = computed(() => this.parking().enabled && !this.visibility(), /* @ts-ignore */
|
|
684
|
+
...(ngDevMode ? [{ debugName: "parked" }] : /* istanbul ignore next */ []));
|
|
685
|
+
fieldTree = computed(() => {
|
|
686
|
+
const candidate = this.rawInputs()['field'];
|
|
687
|
+
return typeof candidate === 'function' ? candidate : undefined;
|
|
688
|
+
}, { ...(ngDevMode ? { debugName: "fieldTree" } : /* istanbul ignore next */ {}), equal: Object.is });
|
|
689
|
+
parkedDomState = computed(() => (this.parked() ? snapshotParkedDomState(this.fieldTree()) : null), { ...(ngDevMode ? { debugName: "parkedDomState" } : /* istanbul ignore next */ {}), equal: isSameParkedDomState });
|
|
378
690
|
constructor() {
|
|
379
691
|
createWrapperChainController({
|
|
380
692
|
vcr: this.vcr,
|
|
@@ -390,1656 +702,549 @@ class DfFieldOutlet {
|
|
|
390
702
|
this.fieldComponent.mountOrReuse(slot, resolved.component, resolved.injector, this.fieldEnvInjector(), this.rawInputs(), this.fieldInputs());
|
|
391
703
|
},
|
|
392
704
|
});
|
|
393
|
-
|
|
705
|
+
explicitEffect([this.rawInputs, this.fieldInputs], ([rawInputs, fieldInputs]) => this.fieldComponent.pushInputs(rawInputs, fieldInputs));
|
|
706
|
+
explicitEffect([this.hidden], ([hidden]) => {
|
|
707
|
+
if (!hidden || !this.eventBus)
|
|
708
|
+
return;
|
|
709
|
+
const resolved = this.dfFieldOutlet();
|
|
710
|
+
const groupContext = resolved.injector.get(GROUP_CONTEXT, null);
|
|
711
|
+
for (const { type, path } of collectInitializingContainers([resolved.fieldDef], groupContext?.groupPath())) {
|
|
712
|
+
emitComponentInitialized(this.eventBus, type, path, this.injector);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
explicitEffect([this.observedElement, this.parking], ([element, parking], onCleanup) => {
|
|
716
|
+
if (!parking.enabled || !element)
|
|
717
|
+
return;
|
|
718
|
+
const bumpEpoch = () => queueMicrotask(() => {
|
|
719
|
+
if (!this.destroyRef.destroyed)
|
|
720
|
+
this.focusEpoch.update((n) => n + 1);
|
|
721
|
+
});
|
|
722
|
+
element.addEventListener('focusin', bumpEpoch);
|
|
723
|
+
element.addEventListener('focusout', bumpEpoch);
|
|
724
|
+
onCleanup(() => {
|
|
725
|
+
element.removeEventListener('focusin', bumpEpoch);
|
|
726
|
+
element.removeEventListener('focusout', bumpEpoch);
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
explicitEffect([this.parked, this.focusEpoch, this.parkedDomState], ([parked, , domState]) => {
|
|
730
|
+
if (parked && this.canPark(domState)) {
|
|
731
|
+
const alreadyParked = this.fieldComponent.parked();
|
|
732
|
+
this.fieldComponent.park();
|
|
733
|
+
if (alreadyParked)
|
|
734
|
+
this.fieldComponent.refresh();
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
this.fieldComponent.unpark();
|
|
738
|
+
}
|
|
739
|
+
});
|
|
394
740
|
this.destroyRef.onDestroy(() => this.fieldComponent.destroyOnTeardown());
|
|
395
741
|
}
|
|
396
|
-
|
|
397
|
-
|
|
742
|
+
canPark(domState) {
|
|
743
|
+
const element = this.observedElement();
|
|
744
|
+
if (!element)
|
|
745
|
+
return false;
|
|
746
|
+
if (element.contains(document.activeElement))
|
|
747
|
+
return false;
|
|
748
|
+
return (domState?.errors.length ?? 0) === 0;
|
|
749
|
+
}
|
|
750
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfFieldOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
751
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.5", type: DfFieldOutlet, isStandalone: true, selector: "[dfFieldOutlet]", inputs: { dfFieldOutlet: { classPropertyName: "dfFieldOutlet", publicName: "dfFieldOutlet", isSignal: true, isRequired: true, transformFunction: null }, dfFieldOutletEnvironmentInjector: { classPropertyName: "dfFieldOutletEnvironmentInjector", publicName: "dfFieldOutletEnvironmentInjector", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
398
752
|
}
|
|
399
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
753
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfFieldOutlet, decorators: [{
|
|
400
754
|
type: Directive,
|
|
401
755
|
args: [{
|
|
402
756
|
selector: '[dfFieldOutlet]',
|
|
403
757
|
}]
|
|
404
758
|
}], ctorParameters: () => [], propDecorators: { dfFieldOutlet: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfFieldOutlet", required: true }] }], dfFieldOutletEnvironmentInjector: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfFieldOutletEnvironmentInjector", required: false }] }] } });
|
|
405
759
|
|
|
760
|
+
function isPending(formSignal) {
|
|
761
|
+
return formSignal()().pending();
|
|
762
|
+
}
|
|
763
|
+
function isTreeValidationErrors(result) {
|
|
764
|
+
const isError = (v) => typeof v === 'object' && v !== null && 'kind' in v;
|
|
765
|
+
return Array.isArray(result) ? result.every(isError) && result.length > 0 : isError(result);
|
|
766
|
+
}
|
|
406
767
|
function wrapSubmissionAction(action) {
|
|
407
768
|
return async (formTree) => {
|
|
408
769
|
const result = action(formTree);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
await Promise.resolve(result);
|
|
770
|
+
const resolved = isObservable(result) ? await firstValueFrom(result) : await Promise.resolve(result);
|
|
771
|
+
return isTreeValidationErrors(resolved) ? resolved : undefined;
|
|
414
772
|
};
|
|
415
773
|
}
|
|
416
774
|
function createSubmissionHandler(options) {
|
|
417
775
|
const { eventBus, configSignal, formSignal, validSignal, logger } = options;
|
|
418
|
-
return eventBus.on('submit').pipe(exhaustMap(() => {
|
|
776
|
+
return eventBus.on('submit').pipe(exhaustMap((event) => {
|
|
777
|
+
const reply = event.reply;
|
|
778
|
+
reply?.accept();
|
|
419
779
|
const submissionConfig = configSignal().submission;
|
|
420
780
|
if (!submissionConfig?.action) {
|
|
781
|
+
reply?.settle(validSignal() ? { status: 'dispatched' } : { status: 'validation-failed' });
|
|
421
782
|
return EMPTY;
|
|
422
783
|
}
|
|
423
784
|
if (!validSignal()) {
|
|
424
785
|
logger.debug('Submission action skipped: form is not valid (invalid or pending async validators)');
|
|
786
|
+
reply?.settle(isPending(formSignal) ? { status: 'pending-validation' } : { status: 'validation-failed' });
|
|
425
787
|
return EMPTY;
|
|
426
788
|
}
|
|
427
789
|
const wrappedAction = wrapSubmissionAction(submissionConfig.action);
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return
|
|
431
|
-
|
|
790
|
+
return defer(() => {
|
|
791
|
+
const formTree = formSignal();
|
|
792
|
+
return from(submit(formTree, wrappedAction)).pipe(
|
|
793
|
+
tap(() => {
|
|
794
|
+
reply?.settle(formTree().errorSummary().length ? { status: 'server-errors' } : { status: 'success' });
|
|
795
|
+
}), catchError((error) => {
|
|
796
|
+
logger.error('Submission action failed:', error);
|
|
797
|
+
reply?.settle({ status: 'action-failed', error });
|
|
798
|
+
return EMPTY;
|
|
799
|
+
}));
|
|
800
|
+
});
|
|
432
801
|
}));
|
|
433
802
|
}
|
|
434
803
|
|
|
435
804
|
class FormSubmitEvent {
|
|
805
|
+
reply;
|
|
436
806
|
type = 'submit';
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
class NextPageEvent {
|
|
440
|
-
type = 'next-page';
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
class PageChangeEvent {
|
|
444
|
-
currentPageIndex;
|
|
445
|
-
totalPages;
|
|
446
|
-
previousPageIndex;
|
|
447
|
-
type = 'page-change';
|
|
448
|
-
constructor(
|
|
449
|
-
currentPageIndex,
|
|
450
|
-
totalPages,
|
|
451
|
-
previousPageIndex) {
|
|
452
|
-
this.currentPageIndex = currentPageIndex;
|
|
453
|
-
this.totalPages = totalPages;
|
|
454
|
-
this.previousPageIndex = previousPageIndex;
|
|
807
|
+
constructor(reply) {
|
|
808
|
+
this.reply = reply;
|
|
455
809
|
}
|
|
456
810
|
}
|
|
457
811
|
|
|
458
|
-
|
|
459
|
-
|
|
812
|
+
const COMPONENT_CACHE = new InjectionToken('COMPONENT_CACHE', {
|
|
813
|
+
providedIn: 'root',
|
|
814
|
+
factory: () => new Map(),
|
|
815
|
+
});
|
|
816
|
+
const COMPONENT_LOAD_CACHE = new InjectionToken('COMPONENT_LOAD_CACHE', {
|
|
817
|
+
providedIn: 'root',
|
|
818
|
+
factory: () => new Map(),
|
|
819
|
+
});
|
|
820
|
+
function injectFieldRegistry() {
|
|
821
|
+
const registry = inject(FIELD_REGISTRY);
|
|
822
|
+
const componentCache = inject(COMPONENT_CACHE);
|
|
823
|
+
const componentLoadCache = inject(COMPONENT_LOAD_CACHE);
|
|
824
|
+
return {
|
|
825
|
+
getType(name) {
|
|
826
|
+
return registry.get(name);
|
|
827
|
+
},
|
|
828
|
+
hasType(name) {
|
|
829
|
+
return registry.has(name);
|
|
830
|
+
},
|
|
831
|
+
async loadTypeComponent(name) {
|
|
832
|
+
const fieldType = registry.get(name);
|
|
833
|
+
if (!fieldType) {
|
|
834
|
+
throw new DynamicFormError(`Field type "${name}" is not registered`);
|
|
835
|
+
}
|
|
836
|
+
if (!fieldType.loadComponent) {
|
|
837
|
+
return undefined;
|
|
838
|
+
}
|
|
839
|
+
const cached = componentCache.get(name);
|
|
840
|
+
if (cached) {
|
|
841
|
+
return cached;
|
|
842
|
+
}
|
|
843
|
+
const pending = componentLoadCache.get(name);
|
|
844
|
+
if (pending) {
|
|
845
|
+
return pending;
|
|
846
|
+
}
|
|
847
|
+
try {
|
|
848
|
+
const load = fieldType
|
|
849
|
+
.loadComponent()
|
|
850
|
+
.then(resolveDefaultExport)
|
|
851
|
+
.then((component) => {
|
|
852
|
+
if (component) {
|
|
853
|
+
componentCache.set(name, component);
|
|
854
|
+
}
|
|
855
|
+
return component;
|
|
856
|
+
})
|
|
857
|
+
.catch((error) => {
|
|
858
|
+
throw new DynamicFormError(`Failed to load component for field type "${name}": ${error}`);
|
|
859
|
+
})
|
|
860
|
+
.finally(() => componentLoadCache.delete(name));
|
|
861
|
+
componentLoadCache.set(name, load);
|
|
862
|
+
return load;
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
throw new DynamicFormError(`Failed to load component for field type "${name}": ${error}`);
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
getLoadedComponent(name) {
|
|
869
|
+
return componentCache.get(name);
|
|
870
|
+
},
|
|
871
|
+
getTypes() {
|
|
872
|
+
return Array.from(registry.values());
|
|
873
|
+
},
|
|
874
|
+
registerTypes(types) {
|
|
875
|
+
types.forEach((type) => {
|
|
876
|
+
registry.set(type.name, type);
|
|
877
|
+
});
|
|
878
|
+
},
|
|
879
|
+
get raw() {
|
|
880
|
+
return registry;
|
|
881
|
+
},
|
|
882
|
+
};
|
|
460
883
|
}
|
|
461
884
|
|
|
462
|
-
|
|
463
|
-
|
|
885
|
+
const NORMALIZED_ARRAY_METADATA = Symbol('normalizedArrayMetadata');
|
|
886
|
+
function setNormalizedArrayMetadata(arrayField, metadata) {
|
|
887
|
+
arrayField[NORMALIZED_ARRAY_METADATA] = metadata;
|
|
464
888
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
type = 'form-clear';
|
|
889
|
+
function getNormalizedArrayMetadata(arrayField) {
|
|
890
|
+
return arrayField[NORMALIZED_ARRAY_METADATA];
|
|
468
891
|
}
|
|
469
892
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
893
|
+
function normalizeSimplifiedArrays(fields) {
|
|
894
|
+
return fields.flatMap((field) => {
|
|
895
|
+
if (isContainerField(field) && !isArrayField(field) && hasChildFields(field)) {
|
|
896
|
+
return [{ ...field, fields: normalizeSimplifiedArrays(field.fields) }];
|
|
897
|
+
}
|
|
898
|
+
if (isArrayField(field) && !isSimplifiedArrayField(field)) {
|
|
899
|
+
const normalizedItems = field.fields.map((item) => Array.isArray(item) ? normalizeSimplifiedArrays(item) : item);
|
|
900
|
+
return [{ ...field, fields: normalizedItems }];
|
|
901
|
+
}
|
|
902
|
+
if (isSimplifiedArrayField(field)) {
|
|
903
|
+
const { arrayField, addButton } = expandSimplifiedArray(field);
|
|
904
|
+
return addButton ? [arrayField, addButton] : [arrayField];
|
|
905
|
+
}
|
|
906
|
+
return [field];
|
|
907
|
+
});
|
|
480
908
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
template;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
909
|
+
const INVALID_ARRAY_CHILD_TYPES = new Set(['page', 'array']);
|
|
910
|
+
function validateSimplifiedTemplate(field) {
|
|
911
|
+
const { template, key } = field;
|
|
912
|
+
const isObjectTemplate = Array.isArray(template);
|
|
913
|
+
if (isObjectTemplate) {
|
|
914
|
+
const templateFields = template;
|
|
915
|
+
for (const tmpl of templateFields) {
|
|
916
|
+
if (INVALID_ARRAY_CHILD_TYPES.has(tmpl.type)) {
|
|
917
|
+
throw new DynamicFormError(`Simplified array "${key}" template contains a '${tmpl.type}' field (key: '${tmpl.key}'). ` +
|
|
918
|
+
`Only leaf fields, rows, and groups are allowed as array children.`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
const singleTemplate = template;
|
|
924
|
+
if (INVALID_ARRAY_CHILD_TYPES.has(singleTemplate.type)) {
|
|
925
|
+
throw new DynamicFormError(`Simplified array "${key}" template has type '${singleTemplate.type}'. ` +
|
|
926
|
+
`Only leaf fields, rows, and groups are allowed as array children.`);
|
|
927
|
+
}
|
|
491
928
|
}
|
|
492
929
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
index,
|
|
502
|
-
template) {
|
|
503
|
-
this.arrayKey = arrayKey;
|
|
504
|
-
this.index = index;
|
|
505
|
-
this.template = template;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
class PopArrayItemEvent {
|
|
510
|
-
arrayKey;
|
|
511
|
-
type = 'pop-array-item';
|
|
512
|
-
constructor(
|
|
513
|
-
arrayKey) {
|
|
514
|
-
this.arrayKey = arrayKey;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
class ShiftArrayItemEvent {
|
|
519
|
-
arrayKey;
|
|
520
|
-
type = 'shift-array-item';
|
|
521
|
-
constructor(
|
|
522
|
-
arrayKey) {
|
|
523
|
-
this.arrayKey = arrayKey;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
class MoveArrayItemEvent {
|
|
528
|
-
arrayKey;
|
|
529
|
-
fromIndex;
|
|
530
|
-
toIndex;
|
|
531
|
-
type = 'move-array-item';
|
|
532
|
-
constructor(
|
|
533
|
-
arrayKey,
|
|
534
|
-
fromIndex,
|
|
535
|
-
toIndex) {
|
|
536
|
-
this.arrayKey = arrayKey;
|
|
537
|
-
this.fromIndex = fromIndex;
|
|
538
|
-
this.toIndex = toIndex;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
class RemoveAtIndexEvent {
|
|
543
|
-
arrayKey;
|
|
544
|
-
index;
|
|
545
|
-
type = 'remove-at-index';
|
|
546
|
-
constructor(
|
|
547
|
-
arrayKey,
|
|
548
|
-
index) {
|
|
549
|
-
this.arrayKey = arrayKey;
|
|
550
|
-
this.index = index;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
class PageNavigationStateChangeEvent {
|
|
555
|
-
state;
|
|
556
|
-
type = 'page-navigation-state-change';
|
|
557
|
-
constructor(state) {
|
|
558
|
-
this.state = state;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
class PageOrchestratorComponent {
|
|
563
|
-
eventBus = inject(EventBus);
|
|
564
|
-
fieldContextRegistry = inject(FieldContextRegistryService);
|
|
565
|
-
functionRegistry = inject(FunctionRegistryService);
|
|
566
|
-
formOptions = inject(FORM_OPTIONS, { optional: true });
|
|
567
|
-
pageFields = input.required(/* @ts-ignore */
|
|
568
|
-
...(ngDevMode ? [{ debugName: "pageFields" }] : /* istanbul ignore next */ []));
|
|
569
|
-
form = input.required(/* @ts-ignore */
|
|
570
|
-
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
571
|
-
fieldSignalContext = input.required(/* @ts-ignore */
|
|
572
|
-
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
573
|
-
pageHiddenStates = computed(() => {
|
|
574
|
-
const pages = this.pageFields();
|
|
575
|
-
return pages.map((page) => this.evaluatePageHidden(page));
|
|
576
|
-
}, /* @ts-ignore */
|
|
577
|
-
...(ngDevMode ? [{ debugName: "pageHiddenStates" }] : /* istanbul ignore next */ []));
|
|
578
|
-
visiblePageIndices = computed(() => {
|
|
579
|
-
const hiddenStates = this.pageHiddenStates();
|
|
580
|
-
return hiddenStates
|
|
581
|
-
.map((hidden, index) => ({ index, hidden }))
|
|
582
|
-
.filter((item) => !item.hidden)
|
|
583
|
-
.map((item) => item.index);
|
|
584
|
-
}, /* @ts-ignore */
|
|
585
|
-
...(ngDevMode ? [{ debugName: "visiblePageIndices" }] : /* istanbul ignore next */ []));
|
|
586
|
-
currentPageIndex = linkedSignal(() => {
|
|
587
|
-
const totalPages = this.pageFields().length;
|
|
588
|
-
if (totalPages === 0)
|
|
589
|
-
return 0;
|
|
590
|
-
return 0;
|
|
591
|
-
}, /* @ts-ignore */
|
|
592
|
-
...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
|
|
593
|
-
state = computed(() => {
|
|
594
|
-
const currentIndex = this.currentPageIndex();
|
|
595
|
-
const totalPages = this.pageFields().length;
|
|
596
|
-
const visibleIndices = this.visiblePageIndices();
|
|
597
|
-
const currentVisiblePosition = visibleIndices.indexOf(currentIndex);
|
|
598
|
-
const isFirstVisiblePage = currentVisiblePosition === 0 || currentVisiblePosition === -1;
|
|
599
|
-
const isLastVisiblePage = currentVisiblePosition >= visibleIndices.length - 1;
|
|
600
|
-
return {
|
|
601
|
-
currentPageIndex: currentIndex,
|
|
602
|
-
totalPages,
|
|
603
|
-
isFirstPage: isFirstVisiblePage,
|
|
604
|
-
isLastPage: isLastVisiblePage,
|
|
605
|
-
navigationDisabled: false,
|
|
606
|
-
};
|
|
607
|
-
}, /* @ts-ignore */
|
|
608
|
-
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
609
|
-
currentPageValid = computed(() => {
|
|
610
|
-
const currentIndex = this.currentPageIndex();
|
|
611
|
-
const pages = this.pageFields();
|
|
612
|
-
const form = this.form();
|
|
613
|
-
if (pages.length === 0 || currentIndex >= pages.length) {
|
|
614
|
-
return true;
|
|
615
|
-
}
|
|
616
|
-
const currentPage = pages[currentIndex];
|
|
617
|
-
const pageFields = currentPage.fields || [];
|
|
618
|
-
const leafKeys = collectLeafFieldKeys(pageFields);
|
|
619
|
-
for (const fieldKey of leafKeys) {
|
|
620
|
-
const field = form[fieldKey];
|
|
621
|
-
if (field && typeof field === 'function') {
|
|
622
|
-
const fieldState = field();
|
|
623
|
-
if (fieldState && typeof fieldState.valid === 'function' && !fieldState.valid()) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return true;
|
|
629
|
-
}, /* @ts-ignore */
|
|
630
|
-
...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
|
|
631
|
-
extendedFieldSignalContext = computed(() => ({
|
|
632
|
-
...this.fieldSignalContext(),
|
|
633
|
-
currentPageValid: this.currentPageValid,
|
|
634
|
-
}), /* @ts-ignore */
|
|
635
|
-
...(ngDevMode ? [{ debugName: "extendedFieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
636
|
-
constructor() {
|
|
637
|
-
this.setupEventListeners();
|
|
638
|
-
explicitEffect([this.state, this.visiblePageIndices], ([state, visibleIndices]) => {
|
|
639
|
-
const currentVisiblePosition = visibleIndices.indexOf(state.currentPageIndex);
|
|
640
|
-
if (currentVisiblePosition === -1 && visibleIndices.length > 0) {
|
|
641
|
-
const nearest = this.findNearestVisiblePage(state.currentPageIndex, visibleIndices);
|
|
642
|
-
if (nearest !== -1) {
|
|
643
|
-
this.navigateToPage(nearest);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
navigateToNextPage() {
|
|
649
|
-
const disableWhenPageInvalid = this.formOptions?.()?.nextButton?.disableWhenPageInvalid ?? true;
|
|
650
|
-
if (disableWhenPageInvalid && !this.currentPageValid()) {
|
|
651
|
-
return {
|
|
652
|
-
success: false,
|
|
653
|
-
newPageIndex: this.state().currentPageIndex,
|
|
654
|
-
error: 'Current page has invalid fields',
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
const currentState = this.state();
|
|
658
|
-
const visibleIndices = this.visiblePageIndices();
|
|
659
|
-
if (currentState.isLastPage) {
|
|
660
|
-
return {
|
|
661
|
-
success: false,
|
|
662
|
-
newPageIndex: currentState.currentPageIndex,
|
|
663
|
-
error: 'Already on the last visible page',
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
if (currentState.navigationDisabled) {
|
|
667
|
-
return {
|
|
668
|
-
success: false,
|
|
669
|
-
newPageIndex: currentState.currentPageIndex,
|
|
670
|
-
error: 'Navigation is currently disabled',
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
const currentVisiblePosition = visibleIndices.indexOf(currentState.currentPageIndex);
|
|
674
|
-
if (currentVisiblePosition === -1 || currentVisiblePosition >= visibleIndices.length - 1) {
|
|
675
|
-
return {
|
|
676
|
-
success: false,
|
|
677
|
-
newPageIndex: currentState.currentPageIndex,
|
|
678
|
-
error: 'No next visible page available',
|
|
679
|
-
};
|
|
680
|
-
}
|
|
681
|
-
const nextVisiblePageIndex = visibleIndices[currentVisiblePosition + 1];
|
|
682
|
-
return this.navigateToPage(nextVisiblePageIndex);
|
|
683
|
-
}
|
|
684
|
-
navigateToPreviousPage() {
|
|
685
|
-
const currentState = this.state();
|
|
686
|
-
const visibleIndices = this.visiblePageIndices();
|
|
687
|
-
if (currentState.isFirstPage) {
|
|
688
|
-
return {
|
|
689
|
-
success: false,
|
|
690
|
-
newPageIndex: currentState.currentPageIndex,
|
|
691
|
-
error: 'Already on the first visible page',
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
if (currentState.navigationDisabled) {
|
|
695
|
-
return {
|
|
696
|
-
success: false,
|
|
697
|
-
newPageIndex: currentState.currentPageIndex,
|
|
698
|
-
error: 'Navigation is currently disabled',
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
const currentVisiblePosition = visibleIndices.indexOf(currentState.currentPageIndex);
|
|
702
|
-
if (currentVisiblePosition <= 0) {
|
|
703
|
-
return {
|
|
704
|
-
success: false,
|
|
705
|
-
newPageIndex: currentState.currentPageIndex,
|
|
706
|
-
error: 'No previous visible page available',
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
const prevVisiblePageIndex = visibleIndices[currentVisiblePosition - 1];
|
|
710
|
-
return this.navigateToPage(prevVisiblePageIndex);
|
|
711
|
-
}
|
|
712
|
-
navigateToPage(pageIndex) {
|
|
713
|
-
const currentState = this.state();
|
|
714
|
-
const totalPages = currentState.totalPages;
|
|
715
|
-
if (pageIndex < 0 || pageIndex >= totalPages) {
|
|
716
|
-
return {
|
|
717
|
-
success: false,
|
|
718
|
-
newPageIndex: currentState.currentPageIndex,
|
|
719
|
-
error: `Invalid page index: ${pageIndex}. Valid range is 0 to ${totalPages - 1}`,
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
const visibleIndices = this.visiblePageIndices();
|
|
723
|
-
if (!visibleIndices.includes(pageIndex)) {
|
|
724
|
-
return {
|
|
725
|
-
success: false,
|
|
726
|
-
newPageIndex: currentState.currentPageIndex,
|
|
727
|
-
error: `Cannot navigate to hidden page at index ${pageIndex}. Visible pages: [${visibleIndices.join(', ')}]`,
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
if (pageIndex === currentState.currentPageIndex) {
|
|
731
|
-
return {
|
|
732
|
-
success: true,
|
|
733
|
-
newPageIndex: pageIndex,
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
const previousIndex = currentState.currentPageIndex;
|
|
737
|
-
this.currentPageIndex.set(pageIndex);
|
|
738
|
-
this.eventBus.dispatch(PageChangeEvent, pageIndex, totalPages, previousIndex);
|
|
739
|
-
return {
|
|
740
|
-
success: true,
|
|
741
|
-
newPageIndex: pageIndex,
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
findNearestVisiblePage(currentIndex, visibleIndices) {
|
|
745
|
-
let nearest = -1;
|
|
746
|
-
let minDistance = Infinity;
|
|
747
|
-
for (const idx of visibleIndices) {
|
|
748
|
-
const distance = Math.abs(idx - currentIndex);
|
|
749
|
-
if (distance < minDistance || (distance === minDistance && idx > nearest)) {
|
|
750
|
-
minDistance = distance;
|
|
751
|
-
nearest = idx;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
return nearest;
|
|
755
|
-
}
|
|
756
|
-
setupEventListeners() {
|
|
757
|
-
this.eventBus
|
|
758
|
-
.on('next-page')
|
|
759
|
-
.pipe(takeUntilDestroyed())
|
|
760
|
-
.subscribe(() => {
|
|
761
|
-
this.navigateToNextPage();
|
|
762
|
-
});
|
|
763
|
-
this.eventBus
|
|
764
|
-
.on('previous-page')
|
|
765
|
-
.pipe(takeUntilDestroyed())
|
|
766
|
-
.subscribe(() => {
|
|
767
|
-
this.navigateToPreviousPage();
|
|
768
|
-
});
|
|
769
|
-
explicitEffect([this.state], ([state]) => this.eventBus.dispatch(PageNavigationStateChangeEvent, state));
|
|
770
|
-
}
|
|
771
|
-
evaluatePageHidden(page) {
|
|
772
|
-
if (!page.logic || page.logic.length === 0) {
|
|
773
|
-
return false;
|
|
774
|
-
}
|
|
775
|
-
const hiddenLogic = page.logic.filter((l) => l.type === 'hidden');
|
|
776
|
-
if (hiddenLogic.length === 0) {
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
for (const logic of hiddenLogic) {
|
|
780
|
-
if (typeof logic.condition === 'boolean') {
|
|
781
|
-
if (logic.condition) {
|
|
782
|
-
return true;
|
|
783
|
-
}
|
|
784
|
-
continue;
|
|
785
|
-
}
|
|
786
|
-
const condition = logic.condition;
|
|
787
|
-
const context = this.fieldContextRegistry.createDisplayOnlyContext(page.key || '', this.functionRegistry.getCustomFunctions());
|
|
788
|
-
if (evaluateCondition(condition, context)) {
|
|
789
|
-
return true;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
return false;
|
|
793
|
-
}
|
|
794
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
795
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: PageOrchestratorComponent, isStandalone: true, selector: "div[page-orchestrator]", inputs: { pageFields: { classPropertyName: "pageFields", publicName: "pageFields", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, fieldSignalContext: { classPropertyName: "fieldSignalContext", publicName: "fieldSignalContext", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-current-page": "state().currentPageIndex", "attr.data-total-pages": "state().totalPages" }, classAttribute: "df-page-orchestrator" }, providers: [
|
|
796
|
-
{
|
|
797
|
-
provide: FIELD_SIGNAL_CONTEXT,
|
|
798
|
-
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
799
|
-
deps: [PageOrchestratorComponent],
|
|
800
|
-
},
|
|
801
|
-
], ngImport: i0, template: `
|
|
802
|
-
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
803
|
-
<!--
|
|
804
|
-
Skip pages hidden by 'hidden' logic conditions entirely. FormStateManager
|
|
805
|
-
derives schema/value/validators from config (not from mounted components),
|
|
806
|
-
so unmounting a hidden page has no effect on form state — only on render +
|
|
807
|
-
CD cost. This is the page-level equivalent of the existing field-level
|
|
808
|
-
\`@if (!field.hidden())\` gate in page-field.component.ts.
|
|
809
|
-
-->
|
|
810
|
-
@if (!pageHiddenStates()[i]) {
|
|
811
|
-
@if (i === state().currentPageIndex || i === state().currentPageIndex + 1 || i === state().currentPageIndex - 1) {
|
|
812
|
-
<!-- Current and adjacent pages (±1): render immediately for flicker-free navigation -->
|
|
813
|
-
@defer (on immediate) {
|
|
814
|
-
<section
|
|
815
|
-
page-field
|
|
816
|
-
[field]="pageField"
|
|
817
|
-
[key]="pageField.key"
|
|
818
|
-
[pageIndex]="i"
|
|
819
|
-
[isVisible]="i === state().currentPageIndex"
|
|
820
|
-
></section>
|
|
821
|
-
} @placeholder {
|
|
822
|
-
<div class="df-page-placeholder" [attr.data-page-index]="i" [attr.data-page-key]="pageField.key"></div>
|
|
823
|
-
}
|
|
824
|
-
} @else {
|
|
825
|
-
<!-- Distant pages: defer until browser is idle for memory savings -->
|
|
826
|
-
@defer (on idle) {
|
|
827
|
-
<section page-field [field]="pageField" [key]="pageField.key" [pageIndex]="i" [isVisible]="false"></section>
|
|
828
|
-
} @placeholder {
|
|
829
|
-
<div class="df-page-placeholder" [attr.data-page-index]="i" [attr.data-page-key]="pageField.key"></div>
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
|
|
835
|
-
import('./ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs').then(m => m.default)], () => [/* @ts-ignore */
|
|
836
|
-
import('./ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs').then(m => m.default)]] });
|
|
837
|
-
}
|
|
838
|
-
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.1", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [/* @ts-ignore */
|
|
839
|
-
import('./ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
840
|
-
type: Component,
|
|
841
|
-
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
842
|
-
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
843
|
-
<!--
|
|
844
|
-
Skip pages hidden by 'hidden' logic conditions entirely. FormStateManager
|
|
845
|
-
derives schema/value/validators from config (not from mounted components),
|
|
846
|
-
so unmounting a hidden page has no effect on form state — only on render +
|
|
847
|
-
CD cost. This is the page-level equivalent of the existing field-level
|
|
848
|
-
\`@if (!field.hidden())\` gate in page-field.component.ts.
|
|
849
|
-
-->
|
|
850
|
-
@if (!pageHiddenStates()[i]) {
|
|
851
|
-
@if (i === state().currentPageIndex || i === state().currentPageIndex + 1 || i === state().currentPageIndex - 1) {
|
|
852
|
-
<!-- Current and adjacent pages (±1): render immediately for flicker-free navigation -->
|
|
853
|
-
@defer (on immediate) {
|
|
854
|
-
<section
|
|
855
|
-
page-field
|
|
856
|
-
[field]="pageField"
|
|
857
|
-
[key]="pageField.key"
|
|
858
|
-
[pageIndex]="i"
|
|
859
|
-
[isVisible]="i === state().currentPageIndex"
|
|
860
|
-
></section>
|
|
861
|
-
} @placeholder {
|
|
862
|
-
<div class="df-page-placeholder" [attr.data-page-index]="i" [attr.data-page-key]="pageField.key"></div>
|
|
863
|
-
}
|
|
864
|
-
} @else {
|
|
865
|
-
<!-- Distant pages: defer until browser is idle for memory savings -->
|
|
866
|
-
@defer (on idle) {
|
|
867
|
-
<section page-field [field]="pageField" [key]="pageField.key" [pageIndex]="i" [isVisible]="false"></section>
|
|
868
|
-
} @placeholder {
|
|
869
|
-
<div class="df-page-placeholder" [attr.data-page-index]="i" [attr.data-page-key]="pageField.key"></div>
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
`, host: {
|
|
875
|
-
class: 'df-page-orchestrator',
|
|
876
|
-
'[attr.data-current-page]': 'state().currentPageIndex',
|
|
877
|
-
'[attr.data-total-pages]': 'state().totalPages',
|
|
878
|
-
}, providers: [
|
|
879
|
-
{
|
|
880
|
-
provide: FIELD_SIGNAL_CONTEXT,
|
|
881
|
-
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
882
|
-
deps: [PageOrchestratorComponent],
|
|
883
|
-
},
|
|
884
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;position:relative;width:100%}\n"] }]
|
|
885
|
-
}], ctorParameters: () => [], propDecorators: { pageFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageFields", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], fieldSignalContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldSignalContext", required: true }] }] } }) });
|
|
886
|
-
function collectLeafFieldKeys(fields) {
|
|
887
|
-
const keys = [];
|
|
888
|
-
for (const field of fields) {
|
|
889
|
-
if (isRowField(field)) {
|
|
890
|
-
keys.push(...collectLeafFieldKeys(field.fields));
|
|
891
|
-
}
|
|
892
|
-
else if (field.key) {
|
|
893
|
-
keys.push(field.key);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
return keys;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
const NORMALIZED_ARRAY_METADATA = Symbol('normalizedArrayMetadata');
|
|
900
|
-
function setNormalizedArrayMetadata(arrayField, metadata) {
|
|
901
|
-
arrayField[NORMALIZED_ARRAY_METADATA] = metadata;
|
|
902
|
-
}
|
|
903
|
-
function getNormalizedArrayMetadata(arrayField) {
|
|
904
|
-
return arrayField[NORMALIZED_ARRAY_METADATA];
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
function normalizeSimplifiedArrays(fields) {
|
|
908
|
-
return fields.flatMap((field) => {
|
|
909
|
-
if (isContainerField(field) && !isArrayField(field) && hasChildFields(field)) {
|
|
910
|
-
return [{ ...field, fields: normalizeSimplifiedArrays(field.fields) }];
|
|
911
|
-
}
|
|
912
|
-
if (isArrayField(field) && !isSimplifiedArrayField(field)) {
|
|
913
|
-
const normalizedItems = field.fields.map((item) => Array.isArray(item) ? normalizeSimplifiedArrays(item) : item);
|
|
914
|
-
return [{ ...field, fields: normalizedItems }];
|
|
915
|
-
}
|
|
916
|
-
if (isSimplifiedArrayField(field)) {
|
|
917
|
-
const { arrayField, addButton } = expandSimplifiedArray(field);
|
|
918
|
-
return addButton ? [arrayField, addButton] : [arrayField];
|
|
919
|
-
}
|
|
920
|
-
return [field];
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
const INVALID_ARRAY_CHILD_TYPES = new Set(['page', 'array']);
|
|
924
|
-
function validateSimplifiedTemplate(field) {
|
|
925
|
-
const { template, key } = field;
|
|
926
|
-
const isObjectTemplate = Array.isArray(template);
|
|
927
|
-
if (isObjectTemplate) {
|
|
928
|
-
const templateFields = template;
|
|
929
|
-
for (const tmpl of templateFields) {
|
|
930
|
-
if (INVALID_ARRAY_CHILD_TYPES.has(tmpl.type)) {
|
|
931
|
-
throw new DynamicFormError(`Simplified array "${key}" template contains a '${tmpl.type}' field (key: '${tmpl.key}'). ` +
|
|
932
|
-
`Only leaf fields, rows, and groups are allowed as array children.`);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
else {
|
|
937
|
-
const singleTemplate = template;
|
|
938
|
-
if (INVALID_ARRAY_CHILD_TYPES.has(singleTemplate.type)) {
|
|
939
|
-
throw new DynamicFormError(`Simplified array "${key}" template has type '${singleTemplate.type}'. ` +
|
|
940
|
-
`Only leaf fields, rows, and groups are allowed as array children.`);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
function expandSimplifiedArray(field) {
|
|
945
|
-
const { template, value = [], addButton, removeButton, key, logic, minLength, maxLength, wrappers, skipAutoWrappers, skipDefaultWrappers, } = field;
|
|
946
|
-
const isObjectTemplate = Array.isArray(template);
|
|
947
|
-
const values = value;
|
|
948
|
-
validateSimplifiedTemplate(field);
|
|
949
|
-
const items = values.map((v) => {
|
|
950
|
-
if (isObjectTemplate) {
|
|
951
|
-
return buildObjectItem(template, v, removeButton);
|
|
952
|
-
}
|
|
953
|
-
return buildPrimitiveItem(template, v);
|
|
954
|
-
});
|
|
955
|
-
const addTemplate = isObjectTemplate
|
|
956
|
-
? buildObjectItemTemplate(template, removeButton)
|
|
957
|
-
: template;
|
|
958
|
-
const hasAutoRemove = !isObjectTemplate && removeButton !== false;
|
|
959
|
-
const arrayFieldObj = {
|
|
960
|
-
key,
|
|
961
|
-
type: 'array',
|
|
962
|
-
fields: items,
|
|
963
|
-
};
|
|
964
|
-
if (logic) {
|
|
965
|
-
arrayFieldObj['logic'] = logic;
|
|
966
|
-
}
|
|
967
|
-
if (minLength !== undefined) {
|
|
968
|
-
arrayFieldObj['minLength'] = minLength;
|
|
969
|
-
}
|
|
970
|
-
if (maxLength !== undefined) {
|
|
971
|
-
arrayFieldObj['maxLength'] = maxLength;
|
|
972
|
-
}
|
|
973
|
-
if (wrappers !== undefined) {
|
|
974
|
-
arrayFieldObj['wrappers'] = wrappers;
|
|
975
|
-
}
|
|
976
|
-
if (skipAutoWrappers !== undefined) {
|
|
977
|
-
arrayFieldObj['skipAutoWrappers'] = skipAutoWrappers;
|
|
978
|
-
}
|
|
979
|
-
if (skipDefaultWrappers !== undefined) {
|
|
980
|
-
arrayFieldObj['skipDefaultWrappers'] = skipDefaultWrappers;
|
|
981
|
-
}
|
|
982
|
-
const arrayField = arrayFieldObj;
|
|
983
|
-
const primitiveFieldKey = !isObjectTemplate ? template.key : undefined;
|
|
984
|
-
const metadataTemplate = isObjectTemplate ? buildObjectItemTemplate(template, removeButton) : template;
|
|
985
|
-
setNormalizedArrayMetadata(arrayFieldObj, {
|
|
986
|
-
...(hasAutoRemove && { autoRemoveButton: buildRemoveButton(removeButton) }),
|
|
987
|
-
...(primitiveFieldKey && { primitiveFieldKey }),
|
|
988
|
-
template: metadataTemplate,
|
|
989
|
-
});
|
|
990
|
-
let addButtonField;
|
|
991
|
-
if (addButton !== false) {
|
|
992
|
-
const buttonConfig = (typeof addButton === 'object' ? addButton : {});
|
|
993
|
-
addButtonField = {
|
|
994
|
-
key: `${key}__add`,
|
|
995
|
-
type: 'add-array-item',
|
|
996
|
-
label: buttonConfig.label ?? 'Add',
|
|
997
|
-
arrayKey: key,
|
|
998
|
-
template: addTemplate,
|
|
999
|
-
...(buttonConfig.props && { props: buttonConfig.props }),
|
|
1000
|
-
...(logic && { logic }),
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
return { arrayField, addButton: addButtonField };
|
|
1004
|
-
}
|
|
1005
|
-
function buildPrimitiveItem(template, value) {
|
|
1006
|
-
return { ...template, value };
|
|
1007
|
-
}
|
|
1008
|
-
function buildObjectItem(template, valueObj, removeButton) {
|
|
1009
|
-
const fieldsWithValues = template.map((templateField) => ({
|
|
1010
|
-
...templateField,
|
|
1011
|
-
...(Object.hasOwn(valueObj, templateField.key) && { value: valueObj[templateField.key] }),
|
|
1012
|
-
}));
|
|
1013
|
-
if (removeButton === false) {
|
|
1014
|
-
return fieldsWithValues;
|
|
1015
|
-
}
|
|
1016
|
-
return [...fieldsWithValues, buildRemoveButton(removeButton)];
|
|
1017
|
-
}
|
|
1018
|
-
function buildObjectItemTemplate(template, removeButton) {
|
|
1019
|
-
if (removeButton === false) {
|
|
1020
|
-
return template;
|
|
1021
|
-
}
|
|
1022
|
-
return [...template, buildRemoveButton(removeButton)];
|
|
1023
|
-
}
|
|
1024
|
-
function buildRemoveButton(config) {
|
|
1025
|
-
const buttonConfig = (typeof config === 'object' ? config : {});
|
|
1026
|
-
return {
|
|
1027
|
-
key: '__remove',
|
|
1028
|
-
type: 'remove-array-item',
|
|
1029
|
-
label: buttonConfig.label ?? 'Remove',
|
|
1030
|
-
...(buttonConfig.props && { props: buttonConfig.props }),
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
function createSlot() {
|
|
1035
|
-
let counter = 0;
|
|
1036
|
-
const templates = new Map();
|
|
1037
|
-
const itemOrder = [];
|
|
1038
|
-
const primitiveFieldKey = signal(undefined, /* @ts-ignore */
|
|
1039
|
-
...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
|
|
1040
|
-
return {
|
|
1041
|
-
templates,
|
|
1042
|
-
itemOrder,
|
|
1043
|
-
primitiveFieldKey,
|
|
1044
|
-
nextId: () => `item-${counter++}`,
|
|
1045
|
-
setTemplate(itemId, t) {
|
|
1046
|
-
templates.set(itemId, t);
|
|
1047
|
-
},
|
|
1048
|
-
getTemplate(itemId) {
|
|
1049
|
-
return templates.get(itemId);
|
|
1050
|
-
},
|
|
1051
|
-
hasTemplate(itemId) {
|
|
1052
|
-
return templates.has(itemId);
|
|
1053
|
-
},
|
|
1054
|
-
setIdAt(index, itemId) {
|
|
1055
|
-
itemOrder[index] = itemId;
|
|
1056
|
-
},
|
|
1057
|
-
removeAt(index) {
|
|
1058
|
-
const [removed] = itemOrder.splice(index, 1);
|
|
1059
|
-
if (removed !== undefined)
|
|
1060
|
-
templates.delete(removed);
|
|
1061
|
-
},
|
|
1062
|
-
moveItem(fromIndex, toIndex) {
|
|
1063
|
-
const [id] = itemOrder.splice(fromIndex, 1);
|
|
1064
|
-
if (id !== undefined)
|
|
1065
|
-
itemOrder.splice(toIndex, 0, id);
|
|
1066
|
-
},
|
|
1067
|
-
truncate(length) {
|
|
1068
|
-
const removed = itemOrder.splice(length);
|
|
1069
|
-
for (const id of removed)
|
|
1070
|
-
templates.delete(id);
|
|
1071
|
-
},
|
|
1072
|
-
clear() {
|
|
1073
|
-
templates.clear();
|
|
1074
|
-
itemOrder.length = 0;
|
|
1075
|
-
counter = 0;
|
|
1076
|
-
primitiveFieldKey.set(undefined);
|
|
1077
|
-
},
|
|
1078
|
-
};
|
|
1079
|
-
}
|
|
1080
|
-
class ArrayItemRegistryService {
|
|
1081
|
-
slots = new Map();
|
|
1082
|
-
slotFor(arrayPath) {
|
|
1083
|
-
let slot = this.slots.get(arrayPath);
|
|
1084
|
-
if (!slot) {
|
|
1085
|
-
slot = createSlot();
|
|
1086
|
-
this.slots.set(arrayPath, slot);
|
|
1087
|
-
}
|
|
1088
|
-
return slot;
|
|
1089
|
-
}
|
|
1090
|
-
clearAll() {
|
|
1091
|
-
for (const slot of this.slots.values())
|
|
1092
|
-
slot.clear();
|
|
1093
|
-
this.slots.clear();
|
|
1094
|
-
}
|
|
1095
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayItemRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1096
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayItemRegistryService });
|
|
1097
|
-
}
|
|
1098
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ArrayItemRegistryService, decorators: [{
|
|
1099
|
-
type: Injectable
|
|
1100
|
-
}] });
|
|
1101
|
-
|
|
1102
|
-
class SchemaRegistryService {
|
|
1103
|
-
registeredSchemas = new Map();
|
|
1104
|
-
registerSchema(schema) {
|
|
1105
|
-
this.registeredSchemas.set(schema.name, schema);
|
|
1106
|
-
}
|
|
1107
|
-
getSchema(name) {
|
|
1108
|
-
return this.registeredSchemas.get(name);
|
|
1109
|
-
}
|
|
1110
|
-
getAllSchemas() {
|
|
1111
|
-
return new Map(this.registeredSchemas);
|
|
1112
|
-
}
|
|
1113
|
-
clearSchemas() {
|
|
1114
|
-
this.registeredSchemas.clear();
|
|
1115
|
-
}
|
|
1116
|
-
resolveSchema(schema) {
|
|
1117
|
-
if (typeof schema === 'string') {
|
|
1118
|
-
return this.getSchema(schema) || null;
|
|
1119
|
-
}
|
|
1120
|
-
return schema;
|
|
1121
|
-
}
|
|
1122
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1123
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: SchemaRegistryService });
|
|
1124
|
-
}
|
|
1125
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: SchemaRegistryService, decorators: [{
|
|
1126
|
-
type: Injectable
|
|
1127
|
-
}] });
|
|
1128
|
-
|
|
1129
|
-
const DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode;
|
|
1130
|
-
|
|
1131
|
-
const DEFAULT_DEBOUNCE_MS = 500;
|
|
1132
|
-
function createDebouncedEffect(source, callback, options) {
|
|
1133
|
-
const { ms = DEFAULT_DEBOUNCE_MS, injector } = options;
|
|
1134
|
-
const source$ = toObservable(source, { injector }).pipe(debounceTime(ms), distinctUntilChanged(), tap((value) => callback(value)));
|
|
1135
|
-
return toSignal(source$, { injector });
|
|
1136
|
-
}
|
|
1137
|
-
function createDebouncedSignal(source, options) {
|
|
1138
|
-
const { ms = DEFAULT_DEBOUNCE_MS, injector } = options;
|
|
1139
|
-
const source$ = toObservable(source, { injector }).pipe(debounceTime(ms), distinctUntilChanged());
|
|
1140
|
-
return toSignal(source$, { injector });
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
function getConfigTrigger(config) {
|
|
1144
|
-
if (!isStateLogicConfig(config)) {
|
|
1145
|
-
return 'onChange';
|
|
1146
|
-
}
|
|
1147
|
-
return config.trigger ?? 'onChange';
|
|
1148
|
-
}
|
|
1149
|
-
function getConfigDebounceMs(config) {
|
|
1150
|
-
if (!isStateLogicConfig(config)) {
|
|
1151
|
-
return undefined;
|
|
1152
|
-
}
|
|
1153
|
-
if (config.trigger === 'debounced') {
|
|
1154
|
-
return config.debounceMs;
|
|
1155
|
-
}
|
|
1156
|
-
return undefined;
|
|
1157
|
-
}
|
|
1158
|
-
function applyLogic(config, fieldPath) {
|
|
1159
|
-
if (isDerivationLogicConfig(config))
|
|
1160
|
-
return;
|
|
1161
|
-
if (!isStateLogicConfig(config)) {
|
|
1162
|
-
if (DEV_MODE) {
|
|
1163
|
-
console.warn(`[Dynamic Forms] Unrecognized logic config type '${config.type}' in applyLogic. ` +
|
|
1164
|
-
'This config will be ignored. If this is a new logic type, ensure it is handled explicitly.');
|
|
1165
|
-
}
|
|
1166
|
-
return;
|
|
1167
|
-
}
|
|
1168
|
-
const path = fieldPath;
|
|
1169
|
-
const trigger = getConfigTrigger(config);
|
|
1170
|
-
const debounceMs = getConfigDebounceMs(config) ?? DEFAULT_DEBOUNCE_MS;
|
|
1171
|
-
if (typeof config.condition === 'boolean') {
|
|
1172
|
-
applyLogicFn(config.type, path, () => config.condition);
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
if (isFormStateCondition(config.condition)) {
|
|
1176
|
-
if (config.condition === 'formInvalid' || config.condition === 'pageInvalid') {
|
|
1177
|
-
const reason = config.condition === 'formInvalid'
|
|
1178
|
-
? `it would create a validation cycle against the form's own validity`
|
|
1179
|
-
: `a value field has no page context for it to resolve against`;
|
|
1180
|
-
throw new DynamicFormError(`The '${config.condition}' form-state condition is not supported on value-field logic because ${reason}. ` +
|
|
1181
|
-
`Use 'formSubmitting', or apply this condition to a button or container instead.`);
|
|
1182
|
-
}
|
|
1183
|
-
applyLogicFn(config.type, path, createFormStateLogicFunction(config.condition));
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
const logicFn = trigger === 'debounced'
|
|
1187
|
-
? createDebouncedLogicFunction(config.condition, debounceMs)
|
|
1188
|
-
: createLogicFunction(config.condition);
|
|
1189
|
-
applyLogicFn(config.type, path, logicFn);
|
|
1190
|
-
}
|
|
1191
|
-
function applyLogicFn(type, path, logicFn) {
|
|
1192
|
-
switch (type) {
|
|
1193
|
-
case 'hidden':
|
|
1194
|
-
hidden(path, logicFn);
|
|
1195
|
-
break;
|
|
1196
|
-
case 'readonly':
|
|
1197
|
-
readonly(path, logicFn);
|
|
1198
|
-
break;
|
|
1199
|
-
case 'disabled':
|
|
1200
|
-
disabled(path, logicFn);
|
|
1201
|
-
break;
|
|
1202
|
-
case 'required':
|
|
1203
|
-
required(path, { when: logicFn });
|
|
1204
|
-
break;
|
|
1205
|
-
default: {
|
|
1206
|
-
const _exhaustive = type;
|
|
1207
|
-
throw new DynamicFormError(`Unhandled state logic type: ${_exhaustive}`);
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
function applyMultipleLogic(configs, fieldPath) {
|
|
1212
|
-
configs.forEach((config) => applyLogic(config, fieldPath));
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
function createTypePredicateFunction(predicate, logger) {
|
|
1216
|
-
return (value) => {
|
|
1217
|
-
try {
|
|
1218
|
-
const result = ExpressionParser.evaluate(predicate, { value });
|
|
1219
|
-
return Boolean(result);
|
|
1220
|
-
}
|
|
1221
|
-
catch (error) {
|
|
1222
|
-
logger.error('Error evaluating type predicate:', predicate, error);
|
|
1223
|
-
return false;
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
function applySchema(config, fieldPath) {
|
|
1229
|
-
const logger = inject(DynamicFormLogger);
|
|
1230
|
-
const schemaRegistry = inject(SchemaRegistryService);
|
|
1231
|
-
const schema = schemaRegistry.resolveSchema(config.schema);
|
|
1232
|
-
if (!schema) {
|
|
1233
|
-
const availableSchemas = Array.from(schemaRegistry.getAllSchemas().keys()).join(', ') || '<none>';
|
|
1234
|
-
logger.error(`Schema not found: '${config.schema}'. ` +
|
|
1235
|
-
`Available schemas: ${availableSchemas}. ` +
|
|
1236
|
-
`Ensure the schema is registered in your schema registry.`);
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
const schemaFn = createSchemaFunction(schema, logger);
|
|
1240
|
-
const path = fieldPath;
|
|
1241
|
-
switch (config.type) {
|
|
1242
|
-
case 'apply':
|
|
1243
|
-
apply(path, schemaFn);
|
|
1244
|
-
break;
|
|
1245
|
-
case 'applyWhen':
|
|
1246
|
-
if (config.condition) {
|
|
1247
|
-
const conditionFn = createLogicFunction(config.condition);
|
|
1248
|
-
applyWhen(path, conditionFn, schemaFn);
|
|
1249
|
-
}
|
|
1250
|
-
break;
|
|
1251
|
-
case 'applyWhenValue':
|
|
1252
|
-
if (config.typePredicate) {
|
|
1253
|
-
const predicate = createTypePredicateFunction(config.typePredicate, logger);
|
|
1254
|
-
applyWhenValue(path, predicate, schemaFn);
|
|
1255
|
-
}
|
|
1256
|
-
break;
|
|
1257
|
-
case 'applyEach':
|
|
1258
|
-
applyEach(path, schemaFn);
|
|
1259
|
-
break;
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
function createSchemaFunction(schema, logger) {
|
|
1263
|
-
return (path) => {
|
|
1264
|
-
schema.validators?.forEach((validatorConfig) => {
|
|
1265
|
-
if (requiresTreeValidation(validatorConfig)) {
|
|
1266
|
-
logger?.warn(`Validator "${validatorConfig.type}" in schema definition "${schema.name}" uses a cross-field expression, ` +
|
|
1267
|
-
`which is not supported on schema definition validators; it was skipped.`);
|
|
1268
|
-
return;
|
|
1269
|
-
}
|
|
1270
|
-
applyValidator(validatorConfig, path);
|
|
1271
|
-
});
|
|
1272
|
-
schema.logic?.forEach((logicConfig) => {
|
|
1273
|
-
applyLogic(logicConfig, path);
|
|
1274
|
-
});
|
|
1275
|
-
schema.subSchemas?.forEach((subSchemaConfig) => {
|
|
1276
|
-
applySchema(subSchemaConfig, path);
|
|
1277
|
-
});
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
function combineAncestorHiddenLogics(logics) {
|
|
1282
|
-
if (logics.length === 0)
|
|
1283
|
-
return undefined;
|
|
1284
|
-
if (logics.length === 1)
|
|
1285
|
-
return logics[0];
|
|
1286
|
-
return (ctx) => logics.some((fn) => fn(ctx));
|
|
1287
|
-
}
|
|
1288
|
-
function resolveFieldOwnContext(fieldDef, parentContext) {
|
|
1289
|
-
if (fieldDef.validateWhenHidden === undefined)
|
|
1290
|
-
return parentContext;
|
|
1291
|
-
if (fieldDef.validateWhenHidden === parentContext.validateWhenHidden)
|
|
1292
|
-
return parentContext;
|
|
1293
|
-
return {
|
|
1294
|
-
...parentContext,
|
|
1295
|
-
validateWhenHidden: fieldDef.validateWhenHidden,
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
function resolveDescendantContext(fieldDef, ownContext) {
|
|
1299
|
-
let ancestorAlwaysHidden = ownContext.ancestorAlwaysHidden;
|
|
1300
|
-
let ancestorHiddenLogics = ownContext.ancestorHiddenLogics;
|
|
1301
|
-
let mutated = false;
|
|
1302
|
-
if (fieldDef.hidden === true && !ancestorAlwaysHidden) {
|
|
1303
|
-
ancestorAlwaysHidden = true;
|
|
1304
|
-
mutated = true;
|
|
1305
|
-
}
|
|
1306
|
-
if (fieldDef.logic) {
|
|
1307
|
-
for (const logic of fieldDef.logic) {
|
|
1308
|
-
if (!isStateLogicConfig(logic) || logic.type !== 'hidden')
|
|
1309
|
-
continue;
|
|
1310
|
-
const condition = logic.condition;
|
|
1311
|
-
if (condition === true) {
|
|
1312
|
-
if (!ancestorAlwaysHidden) {
|
|
1313
|
-
ancestorAlwaysHidden = true;
|
|
1314
|
-
mutated = true;
|
|
1315
|
-
}
|
|
1316
|
-
continue;
|
|
1317
|
-
}
|
|
1318
|
-
if (condition === false)
|
|
1319
|
-
continue;
|
|
1320
|
-
if (typeof condition === 'string')
|
|
1321
|
-
continue;
|
|
1322
|
-
const fn = createLogicFunction(condition);
|
|
1323
|
-
ancestorHiddenLogics = ancestorHiddenLogics.length === 0 ? [fn] : [...ancestorHiddenLogics, fn];
|
|
1324
|
-
mutated = true;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
if (!mutated)
|
|
1328
|
-
return ownContext;
|
|
1329
|
-
return {
|
|
1330
|
-
validateWhenHidden: ownContext.validateWhenHidden,
|
|
1331
|
-
ancestorAlwaysHidden,
|
|
1332
|
-
ancestorHiddenLogics,
|
|
1333
|
-
};
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
const DEFAULT_FIELD_TREE_MAPPING_CONTEXT = {
|
|
1337
|
-
validateWhenHidden: false,
|
|
1338
|
-
ancestorAlwaysHidden: false,
|
|
1339
|
-
ancestorHiddenLogics: [],
|
|
1340
|
-
};
|
|
1341
|
-
function applyStringValidators(path, config) {
|
|
1342
|
-
if (config.email) {
|
|
1343
|
-
email(path);
|
|
1344
|
-
}
|
|
1345
|
-
if (config.minLength !== undefined) {
|
|
1346
|
-
minLength(path, config.minLength);
|
|
1347
|
-
}
|
|
1348
|
-
if (config.maxLength !== undefined) {
|
|
1349
|
-
maxLength(path, config.maxLength);
|
|
1350
|
-
}
|
|
1351
|
-
if (config.pattern) {
|
|
1352
|
-
let regex;
|
|
1353
|
-
if (typeof config.pattern === 'string') {
|
|
1354
|
-
try {
|
|
1355
|
-
regex = new RegExp(config.pattern);
|
|
1356
|
-
}
|
|
1357
|
-
catch (e) {
|
|
1358
|
-
throw new DynamicFormError(`Invalid regex pattern: '${config.pattern}' — ${e instanceof Error ? e.message : String(e)}`);
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
else {
|
|
1362
|
-
regex = config.pattern;
|
|
1363
|
-
}
|
|
1364
|
-
pattern(path, regex);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
function applyNumberValidators(path, config) {
|
|
1368
|
-
if (config.min !== undefined) {
|
|
1369
|
-
min(path, config.min);
|
|
1370
|
-
}
|
|
1371
|
-
if (config.max !== undefined) {
|
|
1372
|
-
max(path, config.max);
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
function getStringValidationConfig(fieldDef) {
|
|
1376
|
-
if (!fieldDef.email && fieldDef.minLength === undefined && fieldDef.maxLength === undefined && !fieldDef.pattern) {
|
|
1377
|
-
return undefined;
|
|
1378
|
-
}
|
|
1379
|
-
return {
|
|
1380
|
-
email: fieldDef.email,
|
|
1381
|
-
minLength: fieldDef.minLength,
|
|
1382
|
-
maxLength: fieldDef.maxLength,
|
|
1383
|
-
pattern: fieldDef.pattern,
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
function getNumberValidationConfig(fieldDef) {
|
|
1387
|
-
const minVal = fieldDef.min ?? fieldDef.minValue;
|
|
1388
|
-
const maxVal = fieldDef.max ?? fieldDef.maxValue;
|
|
1389
|
-
if (minVal === undefined && maxVal === undefined) {
|
|
1390
|
-
return undefined;
|
|
1391
|
-
}
|
|
1392
|
-
return { min: minVal, max: maxVal };
|
|
1393
|
-
}
|
|
1394
|
-
function mapFieldToForm(fieldDef, fieldPath, inheritedContext = DEFAULT_FIELD_TREE_MAPPING_CONTEXT) {
|
|
1395
|
-
const ownContext = resolveFieldOwnContext(fieldDef, inheritedContext);
|
|
1396
|
-
if (isPageField(fieldDef) || isRowField(fieldDef) || isGenericContainerField(fieldDef)) {
|
|
1397
|
-
const descendantContext = resolveDescendantContext(fieldDef, ownContext);
|
|
1398
|
-
mapContainerChildren(fieldDef.fields, fieldPath, descendantContext);
|
|
1399
|
-
return;
|
|
1400
|
-
}
|
|
1401
|
-
if (isGroupField(fieldDef)) {
|
|
1402
|
-
const descendantContext = resolveDescendantContext(fieldDef, ownContext);
|
|
1403
|
-
mapContainerChildren(fieldDef.fields, fieldPath, descendantContext);
|
|
1404
|
-
return;
|
|
1405
|
-
}
|
|
1406
|
-
if (isArrayField(fieldDef)) {
|
|
1407
|
-
const descendantContext = resolveDescendantContext(fieldDef, ownContext);
|
|
1408
|
-
mapArrayFieldToForm(fieldDef, fieldPath, descendantContext);
|
|
1409
|
-
return;
|
|
1410
|
-
}
|
|
1411
|
-
mapLeafField(fieldDef, fieldPath, ownContext);
|
|
1412
|
-
}
|
|
1413
|
-
function mapContainerChildren(fields, parentPath, inheritedContext) {
|
|
1414
|
-
if (!fields)
|
|
1415
|
-
return;
|
|
1416
|
-
const pathRecord = parentPath;
|
|
1417
|
-
for (const field of fields) {
|
|
1418
|
-
if (isPageField(field) || isRowField(field) || isGenericContainerField(field)) {
|
|
1419
|
-
const layoutOwn = resolveFieldOwnContext(field, inheritedContext);
|
|
1420
|
-
const layoutDescendant = resolveDescendantContext(field, layoutOwn);
|
|
1421
|
-
mapContainerChildren(field.fields, parentPath, layoutDescendant);
|
|
1422
|
-
continue;
|
|
1423
|
-
}
|
|
1424
|
-
if (!field.key)
|
|
1425
|
-
continue;
|
|
1426
|
-
const childPath = pathRecord[field.key];
|
|
1427
|
-
if (childPath) {
|
|
1428
|
-
mapFieldToForm(field, childPath, inheritedContext);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
function isValidationStateLogic(config) {
|
|
1433
|
-
return isStateLogicConfig(config) && config.type === 'required';
|
|
1434
|
-
}
|
|
1435
|
-
function mapLeafField(fieldDef, fieldPath, context) {
|
|
1436
|
-
const validationField = fieldDef;
|
|
1437
|
-
const path = fieldPath;
|
|
1438
|
-
applyFieldState(fieldDef, path);
|
|
1439
|
-
if (validationField.logic) {
|
|
1440
|
-
for (const config of validationField.logic) {
|
|
1441
|
-
if (!isValidationStateLogic(config)) {
|
|
1442
|
-
applyLogic(config, fieldPath);
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
if (validationField.schemas) {
|
|
1447
|
-
for (const config of validationField.schemas) {
|
|
1448
|
-
applySchema(config, fieldPath);
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
const applyValidation = (validationPath) => {
|
|
1452
|
-
applySimpleValidationRules(validationField, validationPath);
|
|
1453
|
-
if (validationField.validators) {
|
|
1454
|
-
for (const config of validationField.validators) {
|
|
1455
|
-
applyValidator(config, validationPath);
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
if (validationField.logic) {
|
|
1459
|
-
for (const config of validationField.logic) {
|
|
1460
|
-
if (isValidationStateLogic(config)) {
|
|
1461
|
-
applyLogic(config, validationPath);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
};
|
|
1466
|
-
if (context.validateWhenHidden) {
|
|
1467
|
-
applyValidation(path);
|
|
1468
|
-
return;
|
|
1469
|
-
}
|
|
1470
|
-
if (context.ancestorAlwaysHidden) {
|
|
1471
|
-
return;
|
|
1472
|
-
}
|
|
1473
|
-
const ancestorHiddenLogic = combineAncestorHiddenLogics(context.ancestorHiddenLogics);
|
|
1474
|
-
applyWhen(path, (ctx) => {
|
|
1475
|
-
if (ctx.state.hidden())
|
|
1476
|
-
return false;
|
|
1477
|
-
if (ancestorHiddenLogic && ancestorHiddenLogic(ctx))
|
|
1478
|
-
return false;
|
|
1479
|
-
return true;
|
|
1480
|
-
}, schema((subPath) => applyValidation(subPath)));
|
|
1481
|
-
}
|
|
1482
|
-
function applySimpleValidationRules(fieldDef, path) {
|
|
1483
|
-
if (fieldDef.required) {
|
|
1484
|
-
required(path);
|
|
1485
|
-
}
|
|
1486
|
-
const stringConfig = getStringValidationConfig(fieldDef);
|
|
1487
|
-
if (stringConfig) {
|
|
1488
|
-
applyStringValidators(path, stringConfig);
|
|
1489
|
-
}
|
|
1490
|
-
const numberConfig = getNumberValidationConfig(fieldDef);
|
|
1491
|
-
if (numberConfig) {
|
|
1492
|
-
applyNumberValidators(path, numberConfig);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
function applyFieldState(fieldDef, path) {
|
|
1496
|
-
if (fieldDef.disabled) {
|
|
1497
|
-
disabled(path);
|
|
1498
|
-
}
|
|
1499
|
-
if (fieldDef.readonly) {
|
|
1500
|
-
readonly(path);
|
|
1501
|
-
}
|
|
1502
|
-
if (fieldDef.hidden) {
|
|
1503
|
-
hidden(path, () => true);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
function mapArrayFieldToForm(arrayField, fieldPath, context) {
|
|
1507
|
-
if (!isArrayField(arrayField)) {
|
|
1508
|
-
return;
|
|
1509
|
-
}
|
|
1510
|
-
if (arrayField.minLength !== undefined) {
|
|
1511
|
-
minLength(fieldPath, arrayField.minLength);
|
|
1512
|
-
}
|
|
1513
|
-
if (arrayField.maxLength !== undefined) {
|
|
1514
|
-
maxLength(fieldPath, arrayField.maxLength);
|
|
1515
|
-
}
|
|
1516
|
-
let itemDefinitions = arrayField.fields;
|
|
1517
|
-
if (!itemDefinitions || itemDefinitions.length === 0) {
|
|
1518
|
-
const metadataTemplate = getNormalizedArrayMetadata(arrayField)?.template;
|
|
1519
|
-
if (metadataTemplate) {
|
|
1520
|
-
itemDefinitions = [
|
|
1521
|
-
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
1522
|
-
];
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
if (!itemDefinitions || itemDefinitions.length === 0) {
|
|
1526
|
-
const emptyItemSchema = schema(() => {
|
|
1527
|
-
});
|
|
1528
|
-
applyEach(fieldPath, emptyItemSchema);
|
|
1529
|
-
return;
|
|
1530
|
-
}
|
|
1531
|
-
let hasPrimitiveItems = false;
|
|
1532
|
-
const allObjectFields = [];
|
|
1533
|
-
for (const itemDef of itemDefinitions) {
|
|
1534
|
-
if (Array.isArray(itemDef)) {
|
|
1535
|
-
collectFieldsFromObjectItem(itemDef, allObjectFields);
|
|
1536
|
-
}
|
|
1537
|
-
else if (isPageField(itemDef) ||
|
|
1538
|
-
isRowField(itemDef) ||
|
|
1539
|
-
isGenericContainerField(itemDef)) {
|
|
1540
|
-
collectFieldsFromObjectItem([itemDef], allObjectFields);
|
|
1541
|
-
}
|
|
1542
|
-
else {
|
|
1543
|
-
hasPrimitiveItems = true;
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
if (hasPrimitiveItems && allObjectFields.length === 0) {
|
|
1547
|
-
const primitiveItemSchema = schema(() => {
|
|
1548
|
-
});
|
|
1549
|
-
applyEach(fieldPath, primitiveItemSchema);
|
|
1550
|
-
return;
|
|
1551
|
-
}
|
|
1552
|
-
const itemSchema = schema((itemPath) => {
|
|
1553
|
-
const pathRecord = itemPath;
|
|
1554
|
-
for (const templateField of allObjectFields) {
|
|
1555
|
-
if (isRowField(templateField) || isPageField(templateField) || isGenericContainerField(templateField)) {
|
|
1556
|
-
const layoutOwn = resolveFieldOwnContext(templateField, context);
|
|
1557
|
-
const layoutDescendant = resolveDescendantContext(templateField, layoutOwn);
|
|
1558
|
-
mapContainerChildren(templateField.fields, itemPath, layoutDescendant);
|
|
1559
|
-
}
|
|
1560
|
-
else if (isGroupField(templateField)) {
|
|
1561
|
-
const groupKey = templateField.key;
|
|
1562
|
-
const groupOwn = resolveFieldOwnContext(templateField, context);
|
|
1563
|
-
const groupDescendant = resolveDescendantContext(templateField, groupOwn);
|
|
1564
|
-
if (groupKey) {
|
|
1565
|
-
const groupPath = pathRecord[groupKey];
|
|
1566
|
-
if (groupPath) {
|
|
1567
|
-
mapContainerChildren(templateField.fields, groupPath, groupDescendant);
|
|
1568
|
-
}
|
|
1569
|
-
}
|
|
1570
|
-
else {
|
|
1571
|
-
mapContainerChildren(templateField.fields, itemPath, groupDescendant);
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
else {
|
|
1575
|
-
const fieldKey = templateField.key;
|
|
1576
|
-
if (fieldKey) {
|
|
1577
|
-
const fieldPathForKey = pathRecord[fieldKey];
|
|
1578
|
-
if (fieldPathForKey) {
|
|
1579
|
-
mapFieldToForm(templateField, fieldPathForKey, context);
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
930
|
+
function expandSimplifiedArray(field) {
|
|
931
|
+
const { template, value = [], addButton, removeButton, key, logic, minLength, maxLength, wrappers, skipAutoWrappers, skipDefaultWrappers, validators, validationMessages, required, validateWhenHidden, } = field;
|
|
932
|
+
const isObjectTemplate = Array.isArray(template);
|
|
933
|
+
const values = value;
|
|
934
|
+
validateSimplifiedTemplate(field);
|
|
935
|
+
const items = values.map((v) => {
|
|
936
|
+
if (isObjectTemplate) {
|
|
937
|
+
return buildObjectItem(template, v, removeButton);
|
|
1583
938
|
}
|
|
939
|
+
return buildPrimitiveItem(template, v);
|
|
1584
940
|
});
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
}
|
|
1597
|
-
else {
|
|
1598
|
-
const key = field.key;
|
|
1599
|
-
if (key && !seenKeys.has(key)) {
|
|
1600
|
-
seenKeys.add(key);
|
|
1601
|
-
allFields.push(field);
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
function getFieldDefaultValue(field, registry) {
|
|
1608
|
-
const valueHandling = getFieldValueHandling(field.type, registry);
|
|
1609
|
-
if (valueHandling === 'exclude') {
|
|
1610
|
-
return undefined;
|
|
1611
|
-
}
|
|
1612
|
-
if (valueHandling === 'flatten' && 'fields' in field && field.fields) {
|
|
1613
|
-
const childFields = field.fields;
|
|
1614
|
-
const flattenedValues = {};
|
|
1615
|
-
for (const childField of childFields) {
|
|
1616
|
-
const childValueHandling = getFieldValueHandling(childField.type, registry);
|
|
1617
|
-
if (childValueHandling === 'exclude')
|
|
1618
|
-
continue;
|
|
1619
|
-
const childValue = getFieldDefaultValue(childField, registry);
|
|
1620
|
-
if (childValue === undefined)
|
|
1621
|
-
continue;
|
|
1622
|
-
if (childValueHandling === 'flatten' && childValue && typeof childValue === 'object' && !Array.isArray(childValue)) {
|
|
1623
|
-
Object.assign(flattenedValues, childValue);
|
|
1624
|
-
}
|
|
1625
|
-
else if ('key' in childField && childField.key) {
|
|
1626
|
-
flattenedValues[childField.key] = childValue;
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
return Object.keys(flattenedValues).length > 0 ? flattenedValues : undefined;
|
|
941
|
+
const addTemplate = isObjectTemplate
|
|
942
|
+
? buildObjectItemTemplate(template, removeButton)
|
|
943
|
+
: template;
|
|
944
|
+
const hasAutoRemove = !isObjectTemplate && removeButton !== false;
|
|
945
|
+
const arrayFieldObj = {
|
|
946
|
+
key,
|
|
947
|
+
type: 'array',
|
|
948
|
+
fields: items,
|
|
949
|
+
};
|
|
950
|
+
if (logic) {
|
|
951
|
+
arrayFieldObj['logic'] = logic;
|
|
1630
952
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
if (!fields || fields.length === 0) {
|
|
1634
|
-
return undefined;
|
|
1635
|
-
}
|
|
1636
|
-
const groupDefaults = {};
|
|
1637
|
-
for (const childField of fields) {
|
|
1638
|
-
const childValueHandling = getFieldValueHandling(childField.type, registry);
|
|
1639
|
-
if (childValueHandling === 'exclude')
|
|
1640
|
-
continue;
|
|
1641
|
-
const childValue = getFieldDefaultValue(childField, registry);
|
|
1642
|
-
if (childValueHandling === 'flatten') {
|
|
1643
|
-
if (childValue && typeof childValue === 'object' && !Array.isArray(childValue)) {
|
|
1644
|
-
Object.assign(groupDefaults, childValue);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
else if ('key' in childField && childField.key) {
|
|
1648
|
-
if (childValue !== undefined) {
|
|
1649
|
-
groupDefaults[childField.key] = childValue;
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
return groupDefaults;
|
|
953
|
+
if (minLength !== undefined) {
|
|
954
|
+
arrayFieldObj['minLength'] = minLength;
|
|
1654
955
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
if ('value' in field) {
|
|
1658
|
-
if (valueField.value !== null && valueField.value !== undefined) {
|
|
1659
|
-
return valueField.value;
|
|
1660
|
-
}
|
|
1661
|
-
if (valueField.value === null && isNullable) {
|
|
1662
|
-
return null;
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
if (isNullable) {
|
|
1666
|
-
return null;
|
|
956
|
+
if (maxLength !== undefined) {
|
|
957
|
+
arrayFieldObj['maxLength'] = maxLength;
|
|
1667
958
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
959
|
+
if (wrappers !== undefined) {
|
|
960
|
+
arrayFieldObj['wrappers'] = wrappers;
|
|
1670
961
|
}
|
|
1671
|
-
if (
|
|
1672
|
-
|
|
1673
|
-
const itemDefinitions = arrayField.fields;
|
|
1674
|
-
if (!itemDefinitions || itemDefinitions.length === 0) {
|
|
1675
|
-
return [];
|
|
1676
|
-
}
|
|
1677
|
-
return itemDefinitions.map((itemDef) => {
|
|
1678
|
-
if (!Array.isArray(itemDef)) {
|
|
1679
|
-
return getFieldDefaultValue(itemDef, registry);
|
|
1680
|
-
}
|
|
1681
|
-
const itemFields = itemDef;
|
|
1682
|
-
let itemValue = {};
|
|
1683
|
-
for (const templateField of itemFields) {
|
|
1684
|
-
const fieldValue = getFieldDefaultValue(templateField, registry);
|
|
1685
|
-
const fieldValueHandling = getFieldValueHandling(templateField.type, registry);
|
|
1686
|
-
if (templateField.type === 'group' && 'key' in templateField && templateField.key) {
|
|
1687
|
-
itemValue[templateField.key] = fieldValue;
|
|
1688
|
-
}
|
|
1689
|
-
else if (templateField.type === 'row' || templateField.type === 'container') {
|
|
1690
|
-
if (fieldValue && typeof fieldValue === 'object') {
|
|
1691
|
-
itemValue = { ...itemValue, ...fieldValue };
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
else if (fieldValueHandling === 'include' && 'key' in templateField && templateField.key) {
|
|
1695
|
-
itemValue[templateField.key] = fieldValue;
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
return itemValue;
|
|
1699
|
-
});
|
|
962
|
+
if (skipAutoWrappers !== undefined) {
|
|
963
|
+
arrayFieldObj['skipAutoWrappers'] = skipAutoWrappers;
|
|
1700
964
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
if (props?.type === 'number') {
|
|
1704
|
-
return NaN;
|
|
1705
|
-
}
|
|
965
|
+
if (skipDefaultWrappers !== undefined) {
|
|
966
|
+
arrayFieldObj['skipDefaultWrappers'] = skipDefaultWrappers;
|
|
1706
967
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
function applyFormLevelSchema(path, formLevelSchema) {
|
|
1711
|
-
if (isStandardSchemaMarker(formLevelSchema)) {
|
|
1712
|
-
validateStandardSchema(path, formLevelSchema.schema);
|
|
968
|
+
if (validators !== undefined) {
|
|
969
|
+
arrayFieldObj['validators'] = validators;
|
|
1713
970
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
971
|
+
if (validationMessages !== undefined) {
|
|
972
|
+
arrayFieldObj['validationMessages'] = validationMessages;
|
|
1716
973
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
974
|
+
if (required !== undefined) {
|
|
975
|
+
arrayFieldObj['required'] = required;
|
|
976
|
+
}
|
|
977
|
+
if (validateWhenHidden !== undefined) {
|
|
978
|
+
arrayFieldObj['validateWhenHidden'] = validateWhenHidden;
|
|
979
|
+
}
|
|
980
|
+
const arrayField = arrayFieldObj;
|
|
981
|
+
const primitiveFieldKey = !isObjectTemplate ? template.key : undefined;
|
|
982
|
+
const metadataTemplate = isObjectTemplate ? buildObjectItemTemplate(template, removeButton) : template;
|
|
983
|
+
setNormalizedArrayMetadata(arrayFieldObj, {
|
|
984
|
+
...(hasAutoRemove && { autoRemoveButton: buildRemoveButton(removeButton) }),
|
|
985
|
+
...(primitiveFieldKey && { primitiveFieldKey }),
|
|
986
|
+
template: metadataTemplate,
|
|
1721
987
|
});
|
|
988
|
+
let addButtonField;
|
|
989
|
+
if (addButton !== false) {
|
|
990
|
+
const buttonConfig = (typeof addButton === 'object' ? addButton : {});
|
|
991
|
+
addButtonField = {
|
|
992
|
+
key: `${key}__add`,
|
|
993
|
+
type: 'add-array-item',
|
|
994
|
+
label: buttonConfig.label ?? 'Add',
|
|
995
|
+
arrayKey: key,
|
|
996
|
+
template: addTemplate,
|
|
997
|
+
...(buttonConfig.props && { props: buttonConfig.props }),
|
|
998
|
+
...(logic && { logic }),
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
return { arrayField, addButton: addButtonField };
|
|
1722
1002
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
})
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
return ctx.fieldTree[key];
|
|
1734
|
-
}
|
|
1735
|
-
const parts = key.split('.');
|
|
1736
|
-
let current = ctx.fieldTree;
|
|
1737
|
-
for (const part of parts) {
|
|
1738
|
-
if (!current || (typeof current !== 'object' && typeof current !== 'function')) {
|
|
1739
|
-
return undefined;
|
|
1740
|
-
}
|
|
1741
|
-
current = current[part];
|
|
1003
|
+
function buildPrimitiveItem(template, value) {
|
|
1004
|
+
return { ...template, value };
|
|
1005
|
+
}
|
|
1006
|
+
function buildObjectItem(template, valueObj, removeButton) {
|
|
1007
|
+
const fieldsWithValues = template.map((templateField) => ({
|
|
1008
|
+
...templateField,
|
|
1009
|
+
...(Object.hasOwn(valueObj, templateField.key) && { value: valueObj[templateField.key] }),
|
|
1010
|
+
}));
|
|
1011
|
+
if (removeButton === false) {
|
|
1012
|
+
return fieldsWithValues;
|
|
1742
1013
|
}
|
|
1743
|
-
return
|
|
1014
|
+
return [...fieldsWithValues, buildRemoveButton(removeButton)];
|
|
1744
1015
|
}
|
|
1745
|
-
function
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
const
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1016
|
+
function buildObjectItemTemplate(template, removeButton) {
|
|
1017
|
+
if (removeButton === false) {
|
|
1018
|
+
return template;
|
|
1019
|
+
}
|
|
1020
|
+
return [...template, buildRemoveButton(removeButton)];
|
|
1021
|
+
}
|
|
1022
|
+
function buildRemoveButton(config) {
|
|
1023
|
+
const buttonConfig = (typeof config === 'object' ? config : {});
|
|
1024
|
+
return {
|
|
1025
|
+
key: '__remove',
|
|
1026
|
+
type: 'remove-array-item',
|
|
1027
|
+
label: buttonConfig.label ?? 'Remove',
|
|
1028
|
+
...(buttonConfig.props && { props: buttonConfig.props }),
|
|
1757
1029
|
};
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
const childPath = path[childField.key];
|
|
1771
|
-
if (childPath) {
|
|
1772
|
-
mapFieldToForm(childField, childPath, flattenedContext);
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
continue;
|
|
1776
|
-
}
|
|
1777
|
-
const fieldPath = path[fieldDef.key];
|
|
1778
|
-
if (!fieldPath) {
|
|
1779
|
-
continue;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function collectFieldDefs(fields, out = []) {
|
|
1033
|
+
for (const field of fields ?? []) {
|
|
1034
|
+
out.push(field);
|
|
1035
|
+
const children = field.fields;
|
|
1036
|
+
if (Array.isArray(children)) {
|
|
1037
|
+
for (const child of children) {
|
|
1038
|
+
if (Array.isArray(child))
|
|
1039
|
+
collectFieldDefs(child, out);
|
|
1040
|
+
else
|
|
1041
|
+
collectFieldDefs([child], out);
|
|
1780
1042
|
}
|
|
1781
|
-
mapFieldToForm(fieldDef, fieldPath, rootContext);
|
|
1782
1043
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1044
|
+
const metadata = getNormalizedArrayMetadata(field);
|
|
1045
|
+
const template = metadata?.template;
|
|
1046
|
+
if (Array.isArray(template))
|
|
1047
|
+
collectFieldDefs(template, out);
|
|
1048
|
+
else if (template)
|
|
1049
|
+
collectFieldDefs([template], out);
|
|
1050
|
+
if (metadata?.autoRemoveButton)
|
|
1051
|
+
collectFieldDefs([metadata.autoRemoveButton], out);
|
|
1052
|
+
}
|
|
1053
|
+
return out;
|
|
1054
|
+
}
|
|
1055
|
+
function injectFormComponentPreloader() {
|
|
1056
|
+
const fieldRegistry = injectFieldRegistry();
|
|
1057
|
+
const wrapperRegistry = inject(WRAPPER_REGISTRY);
|
|
1058
|
+
const wrapperCache = inject(WRAPPER_COMPONENT_CACHE);
|
|
1059
|
+
const wrapperLoadCache = inject(WRAPPER_COMPONENT_LOAD_CACHE);
|
|
1060
|
+
const autoAssociations = inject(WRAPPER_AUTO_ASSOCIATIONS);
|
|
1061
|
+
const defaultWrappers = inject(DEFAULT_WRAPPERS, { optional: true });
|
|
1062
|
+
const preloadedConfigs = new WeakSet();
|
|
1063
|
+
const preloadedFieldRoots = new WeakSet();
|
|
1064
|
+
const preloadFields = (fields) => {
|
|
1065
|
+
const unseenFields = fields.filter((field) => {
|
|
1066
|
+
if (preloadedFieldRoots.has(field))
|
|
1067
|
+
return false;
|
|
1068
|
+
preloadedFieldRoots.add(field);
|
|
1069
|
+
return true;
|
|
1070
|
+
});
|
|
1071
|
+
if (unseenFields.length === 0)
|
|
1072
|
+
return;
|
|
1073
|
+
const normalizedFields = normalizeSimplifiedArrays(unseenFields);
|
|
1074
|
+
const defs = collectFieldDefs(normalizedFields);
|
|
1075
|
+
if (defs.length === 0)
|
|
1076
|
+
return;
|
|
1077
|
+
const fieldTypes = new Set();
|
|
1078
|
+
const wrapperTypes = new Set();
|
|
1079
|
+
for (const def of defs) {
|
|
1080
|
+
if (def.type)
|
|
1081
|
+
fieldTypes.add(def.type);
|
|
1082
|
+
for (const wrapper of resolveWrappers(def, defaultWrappers?.(), autoAssociations, wrapperRegistry)) {
|
|
1083
|
+
wrapperTypes.add(wrapper.type);
|
|
1084
|
+
}
|
|
1788
1085
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
if (entry.config.type !== 'custom') {
|
|
1795
|
-
logger.warn(`Unexpected non-custom validator in cross-field tree for "${entry.sourceFieldKey}"; ignoring.`);
|
|
1796
|
-
return false;
|
|
1086
|
+
const started = [];
|
|
1087
|
+
for (const type of fieldTypes) {
|
|
1088
|
+
if (fieldRegistry.getLoadedComponent(type))
|
|
1089
|
+
continue;
|
|
1090
|
+
started.push(Promise.resolve(fieldRegistry.loadTypeComponent(type)).catch(() => undefined));
|
|
1797
1091
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
return null; }
|
|
1803
|
-
const formValue = ctx.value();
|
|
1804
|
-
const errors = [];
|
|
1805
|
-
for (const entry of customValidators) {
|
|
1806
|
-
const { sourceFieldKey, config } = entry;
|
|
1807
|
-
try {
|
|
1808
|
-
const error = evaluateCrossFieldValidator(entry, formValue, sourceFieldKey, ctx, customFunctions, logger);
|
|
1809
|
-
if (error) {
|
|
1810
|
-
errors.push(error);
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
catch (err) {
|
|
1814
|
-
logger.error(`Error evaluating cross-field validator for ${sourceFieldKey}:`, err);
|
|
1815
|
-
const targetField = getFieldTreeByKey(ctx, sourceFieldKey);
|
|
1816
|
-
if (targetField) {
|
|
1817
|
-
const customConfig = config;
|
|
1818
|
-
errors.push({
|
|
1819
|
-
kind: customConfig.kind || config.type || 'custom',
|
|
1820
|
-
fieldTree: targetField,
|
|
1821
|
-
});
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1092
|
+
for (const type of wrapperTypes) {
|
|
1093
|
+
if (wrapperCache.has(type))
|
|
1094
|
+
continue;
|
|
1095
|
+
started.push(loadWrapperComponent(type, wrapperRegistry, wrapperCache, wrapperLoadCache).catch(() => undefined));
|
|
1824
1096
|
}
|
|
1825
|
-
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1097
|
+
void Promise.allSettled(started);
|
|
1098
|
+
};
|
|
1099
|
+
return {
|
|
1100
|
+
preloadConfig: (config) => {
|
|
1101
|
+
if (preloadedConfigs.has(config))
|
|
1102
|
+
return;
|
|
1103
|
+
preloadedConfigs.add(config);
|
|
1104
|
+
const fields = config.fields;
|
|
1105
|
+
if (fields.length > 0 && fields.every(isPageField))
|
|
1106
|
+
return;
|
|
1107
|
+
preloadFields(fields);
|
|
1108
|
+
},
|
|
1109
|
+
preloadFields,
|
|
1837
1110
|
};
|
|
1838
|
-
return evaluateCustomCrossFieldValidator(config, evaluationContext, sourceFieldKey, ctx);
|
|
1839
1111
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
if (!conditionMet) {
|
|
1854
|
-
return null; }
|
|
1855
|
-
}
|
|
1856
|
-
const result = ExpressionParser.evaluate(config.expression, evaluationContext);
|
|
1857
|
-
if (result) {
|
|
1858
|
-
return null;
|
|
1859
|
-
}
|
|
1860
|
-
const targetField = getFieldTreeByKey(ctx, sourceFieldKey);
|
|
1861
|
-
if (!targetField) {
|
|
1862
|
-
evaluationContext.logger.warn(`Cross-field validator references non-existent field "${sourceFieldKey}"`);
|
|
1863
|
-
return null;
|
|
1112
|
+
|
|
1113
|
+
class PageChangeEvent {
|
|
1114
|
+
currentPageIndex;
|
|
1115
|
+
totalPages;
|
|
1116
|
+
previousPageIndex;
|
|
1117
|
+
type = 'page-change';
|
|
1118
|
+
constructor(
|
|
1119
|
+
currentPageIndex,
|
|
1120
|
+
totalPages,
|
|
1121
|
+
previousPageIndex) {
|
|
1122
|
+
this.currentPageIndex = currentPageIndex;
|
|
1123
|
+
this.totalPages = totalPages;
|
|
1124
|
+
this.previousPageIndex = previousPageIndex;
|
|
1864
1125
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
errorObj[key] = ExpressionParser.evaluate(expression, evaluationContext);
|
|
1873
|
-
}
|
|
1874
|
-
catch (error) {
|
|
1875
|
-
evaluationContext.logger.warn(`Failed to evaluate error param expression "${key}": ${expression}`, error);
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
class PagerStateEvent {
|
|
1129
|
+
state;
|
|
1130
|
+
type = 'pager-state';
|
|
1131
|
+
constructor(state) {
|
|
1132
|
+
this.state = state;
|
|
1878
1133
|
}
|
|
1879
|
-
return errorObj;
|
|
1880
1134
|
}
|
|
1881
|
-
|
|
1882
|
-
|
|
1135
|
+
|
|
1136
|
+
const PAGE_PRELOAD_WINDOW = new InjectionToken('PAGE_PRELOAD_WINDOW', {
|
|
1137
|
+
providedIn: 'root',
|
|
1138
|
+
factory: () => 1,
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
function collectLeafFieldKeys(fields) {
|
|
1142
|
+
const keys = [];
|
|
1883
1143
|
for (const field of fields) {
|
|
1884
|
-
if (
|
|
1885
|
-
|
|
1886
|
-
const valueHandling = getFieldValueHandling(field.type, registry);
|
|
1887
|
-
if (valueHandling === 'flatten') {
|
|
1888
|
-
continue;
|
|
1144
|
+
if (isRowField(field) || isGenericContainerField(field)) {
|
|
1145
|
+
keys.push(...collectLeafFieldKeys(field.fields));
|
|
1889
1146
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
defaultValues[field.key] = value;
|
|
1147
|
+
else if (field.key) {
|
|
1148
|
+
keys.push(field.key);
|
|
1893
1149
|
}
|
|
1894
1150
|
}
|
|
1895
|
-
return
|
|
1151
|
+
return keys;
|
|
1896
1152
|
}
|
|
1897
1153
|
|
|
1898
|
-
function
|
|
1154
|
+
function createSlot() {
|
|
1155
|
+
let counter = 0;
|
|
1156
|
+
const templates = new Map();
|
|
1157
|
+
const itemOrder = [];
|
|
1158
|
+
const primitiveFieldKey = signal(undefined, /* @ts-ignore */
|
|
1159
|
+
...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
|
|
1899
1160
|
return {
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1161
|
+
templates,
|
|
1162
|
+
itemOrder,
|
|
1163
|
+
primitiveFieldKey,
|
|
1164
|
+
nextId: () => `item-${counter++}`,
|
|
1165
|
+
setTemplate(itemId, t) {
|
|
1166
|
+
templates.set(itemId, t);
|
|
1167
|
+
},
|
|
1168
|
+
getTemplate(itemId) {
|
|
1169
|
+
return templates.get(itemId);
|
|
1170
|
+
},
|
|
1171
|
+
hasTemplate(itemId) {
|
|
1172
|
+
return templates.has(itemId);
|
|
1173
|
+
},
|
|
1174
|
+
setIdAt(index, itemId) {
|
|
1175
|
+
itemOrder[index] = itemId;
|
|
1176
|
+
},
|
|
1177
|
+
removeAt(index) {
|
|
1178
|
+
const [removed] = itemOrder.splice(index, 1);
|
|
1179
|
+
if (removed !== undefined)
|
|
1180
|
+
templates.delete(removed);
|
|
1181
|
+
},
|
|
1182
|
+
moveItem(fromIndex, toIndex) {
|
|
1183
|
+
const [id] = itemOrder.splice(fromIndex, 1);
|
|
1184
|
+
if (id !== undefined)
|
|
1185
|
+
itemOrder.splice(toIndex, 0, id);
|
|
1186
|
+
},
|
|
1187
|
+
truncate(length) {
|
|
1188
|
+
const removed = itemOrder.splice(length);
|
|
1189
|
+
for (const id of removed)
|
|
1190
|
+
templates.delete(id);
|
|
1191
|
+
},
|
|
1192
|
+
clear() {
|
|
1193
|
+
templates.clear();
|
|
1194
|
+
itemOrder.length = 0;
|
|
1195
|
+
counter = 0;
|
|
1196
|
+
primitiveFieldKey.set(undefined);
|
|
1197
|
+
},
|
|
1903
1198
|
};
|
|
1904
1199
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
collectFromField(field, collection, pathPrefix);
|
|
1913
|
-
if (hasChildFields(field)) {
|
|
1914
|
-
const groupKey = isGroupField(field) ? field.key : undefined;
|
|
1915
|
-
const childPrefix = groupKey ? (pathPrefix ? `${pathPrefix}.${groupKey}` : groupKey) : pathPrefix;
|
|
1916
|
-
traverseFields(normalizeFieldsArray(field.fields), collection, childPrefix);
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
function collectFromField(field, collection, pathPrefix) {
|
|
1921
|
-
const fieldKey = field.key;
|
|
1922
|
-
if (!fieldKey)
|
|
1923
|
-
return;
|
|
1924
|
-
const fullKey = pathPrefix ? `${pathPrefix}.${fieldKey}` : fieldKey;
|
|
1925
|
-
const validationField = field;
|
|
1926
|
-
if (validationField.validators) {
|
|
1927
|
-
for (const config of validationField.validators) {
|
|
1928
|
-
const entry = tryCreateValidatorEntry(fullKey, config);
|
|
1929
|
-
if (entry) {
|
|
1930
|
-
collection.validators.push(entry);
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
if (validationField.logic) {
|
|
1935
|
-
for (const config of validationField.logic) {
|
|
1936
|
-
const entry = tryCreateLogicEntry(fullKey, config);
|
|
1937
|
-
if (entry) {
|
|
1938
|
-
collection.logic.push(entry);
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
if (validationField.schemas) {
|
|
1943
|
-
for (const config of validationField.schemas) {
|
|
1944
|
-
const entry = tryCreateSchemaEntry(fullKey, config);
|
|
1945
|
-
if (entry) {
|
|
1946
|
-
collection.schemas.push(entry);
|
|
1947
|
-
}
|
|
1200
|
+
class ArrayItemRegistryService {
|
|
1201
|
+
slots = new Map();
|
|
1202
|
+
slotFor(arrayPath) {
|
|
1203
|
+
let slot = this.slots.get(arrayPath);
|
|
1204
|
+
if (!slot) {
|
|
1205
|
+
slot = createSlot();
|
|
1206
|
+
this.slots.set(arrayPath, slot);
|
|
1948
1207
|
}
|
|
1208
|
+
return slot;
|
|
1949
1209
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
if (config.type === 'custom') {
|
|
1955
|
-
const customConfig = config;
|
|
1956
|
-
return {
|
|
1957
|
-
sourceFieldKey: fieldKey,
|
|
1958
|
-
config,
|
|
1959
|
-
dependsOn: extractStringDependencies(customConfig.expression || ''),
|
|
1960
|
-
category: 'validator',
|
|
1961
|
-
};
|
|
1210
|
+
clearAll() {
|
|
1211
|
+
for (const slot of this.slots.values())
|
|
1212
|
+
slot.clear();
|
|
1213
|
+
this.slots.clear();
|
|
1962
1214
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
sourceFieldKey: fieldKey,
|
|
1966
|
-
config: convertBuiltInToCustomValidator(builtInConfig),
|
|
1967
|
-
validatorType: config.type,
|
|
1968
|
-
dependsOn: extractStringDependencies(builtInConfig.expression || ''),
|
|
1969
|
-
category: 'validator',
|
|
1970
|
-
};
|
|
1215
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ArrayItemRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1216
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ArrayItemRegistryService });
|
|
1971
1217
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1218
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ArrayItemRegistryService, decorators: [{
|
|
1219
|
+
type: Injectable
|
|
1220
|
+
}] });
|
|
1221
|
+
|
|
1222
|
+
class SchemaRegistryService {
|
|
1223
|
+
registeredSchemas = new Map();
|
|
1224
|
+
registerSchema(schema) {
|
|
1225
|
+
this.registeredSchemas.set(schema.name, schema);
|
|
1975
1226
|
}
|
|
1976
|
-
|
|
1977
|
-
return
|
|
1227
|
+
getSchema(name) {
|
|
1228
|
+
return this.registeredSchemas.get(name);
|
|
1978
1229
|
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
sourceFieldKey: fieldKey,
|
|
1982
|
-
logicType: config.type,
|
|
1983
|
-
condition,
|
|
1984
|
-
config: config,
|
|
1985
|
-
dependsOn: extractExpressionDependencies(condition),
|
|
1986
|
-
category: 'logic',
|
|
1987
|
-
};
|
|
1988
|
-
}
|
|
1989
|
-
function tryCreateSchemaEntry(fieldKey, config) {
|
|
1990
|
-
if (!isCrossFieldSchema(config)) {
|
|
1991
|
-
return null;
|
|
1230
|
+
getAllSchemas() {
|
|
1231
|
+
return new Map(this.registeredSchemas);
|
|
1992
1232
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
sourceFieldKey: fieldKey,
|
|
1996
|
-
config,
|
|
1997
|
-
condition,
|
|
1998
|
-
dependsOn: extractExpressionDependencies(condition),
|
|
1999
|
-
category: 'schema',
|
|
2000
|
-
};
|
|
2001
|
-
}
|
|
2002
|
-
function convertBuiltInToCustomValidator(config) {
|
|
2003
|
-
const expression = config.expression;
|
|
2004
|
-
if (!expression) {
|
|
2005
|
-
throw new DynamicFormError(`Built-in validator ${config.type} missing required expression for cross-field conversion`);
|
|
2006
|
-
}
|
|
2007
|
-
let validationExpression;
|
|
2008
|
-
switch (config.type) {
|
|
2009
|
-
case 'min':
|
|
2010
|
-
validationExpression = `fieldValue == null || fieldValue >= (${expression})`;
|
|
2011
|
-
break;
|
|
2012
|
-
case 'max':
|
|
2013
|
-
validationExpression = `fieldValue == null || fieldValue <= (${expression})`;
|
|
2014
|
-
break;
|
|
2015
|
-
case 'minLength':
|
|
2016
|
-
validationExpression = `fieldValue == null || (typeof fieldValue !== 'string' && !Array.isArray(fieldValue)) || fieldValue.length >= (${expression})`;
|
|
2017
|
-
break;
|
|
2018
|
-
case 'maxLength':
|
|
2019
|
-
validationExpression = `fieldValue == null || (typeof fieldValue !== 'string' && !Array.isArray(fieldValue)) || fieldValue.length <= (${expression})`;
|
|
2020
|
-
break;
|
|
2021
|
-
case 'pattern':
|
|
2022
|
-
try {
|
|
2023
|
-
new RegExp(expression);
|
|
2024
|
-
}
|
|
2025
|
-
catch (e) {
|
|
2026
|
-
throw new DynamicFormError(`Invalid regex pattern in cross-field validator: '${expression}' — ${e instanceof Error ? e.message : String(e)}`);
|
|
2027
|
-
}
|
|
2028
|
-
validationExpression = `fieldValue == null || new RegExp(${JSON.stringify(expression)}).test(fieldValue)`;
|
|
2029
|
-
break;
|
|
2030
|
-
default:
|
|
2031
|
-
throw new DynamicFormError(`Cannot convert ${config.type} validator to custom validator`);
|
|
1233
|
+
clearSchemas() {
|
|
1234
|
+
this.registeredSchemas.clear();
|
|
2032
1235
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
};
|
|
1236
|
+
resolveSchema(schema) {
|
|
1237
|
+
if (typeof schema === 'string') {
|
|
1238
|
+
return this.getSchema(schema) || null;
|
|
1239
|
+
}
|
|
1240
|
+
return schema;
|
|
1241
|
+
}
|
|
1242
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1243
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: SchemaRegistryService });
|
|
2042
1244
|
}
|
|
1245
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: SchemaRegistryService, decorators: [{
|
|
1246
|
+
type: Injectable
|
|
1247
|
+
}] });
|
|
2043
1248
|
|
|
2044
1249
|
function inheritContainerExclusion(container, child) {
|
|
2045
1250
|
const inheritedHidden = container.hidden === true || child.inheritedExclusionState?.hidden === true;
|
|
@@ -2119,6 +1324,109 @@ function flattenFields(fields, registry, options = {}) {
|
|
|
2119
1324
|
return result;
|
|
2120
1325
|
}
|
|
2121
1326
|
|
|
1327
|
+
function getFieldDefaultValue(field, registry) {
|
|
1328
|
+
const valueHandling = getFieldValueHandling(field.type, registry);
|
|
1329
|
+
if (valueHandling === 'exclude') {
|
|
1330
|
+
return undefined;
|
|
1331
|
+
}
|
|
1332
|
+
if (valueHandling === 'flatten' && 'fields' in field && field.fields) {
|
|
1333
|
+
const childFields = field.fields;
|
|
1334
|
+
const flattenedValues = {};
|
|
1335
|
+
for (const childField of childFields) {
|
|
1336
|
+
const childValueHandling = getFieldValueHandling(childField.type, registry);
|
|
1337
|
+
if (childValueHandling === 'exclude')
|
|
1338
|
+
continue;
|
|
1339
|
+
const childValue = getFieldDefaultValue(childField, registry);
|
|
1340
|
+
if (childValue === undefined)
|
|
1341
|
+
continue;
|
|
1342
|
+
if (childValueHandling === 'flatten' && childValue && typeof childValue === 'object' && !Array.isArray(childValue)) {
|
|
1343
|
+
Object.assign(flattenedValues, childValue);
|
|
1344
|
+
}
|
|
1345
|
+
else if ('key' in childField && childField.key) {
|
|
1346
|
+
flattenedValues[childField.key] = childValue;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
return Object.keys(flattenedValues).length > 0 ? flattenedValues : undefined;
|
|
1350
|
+
}
|
|
1351
|
+
if (field.type === 'group' && 'fields' in field) {
|
|
1352
|
+
const fields = field.fields;
|
|
1353
|
+
if (!fields || fields.length === 0) {
|
|
1354
|
+
return undefined;
|
|
1355
|
+
}
|
|
1356
|
+
const groupDefaults = {};
|
|
1357
|
+
for (const childField of fields) {
|
|
1358
|
+
const childValueHandling = getFieldValueHandling(childField.type, registry);
|
|
1359
|
+
if (childValueHandling === 'exclude')
|
|
1360
|
+
continue;
|
|
1361
|
+
const childValue = getFieldDefaultValue(childField, registry);
|
|
1362
|
+
if (childValueHandling === 'flatten') {
|
|
1363
|
+
if (childValue && typeof childValue === 'object' && !Array.isArray(childValue)) {
|
|
1364
|
+
Object.assign(groupDefaults, childValue);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
else if ('key' in childField && childField.key) {
|
|
1368
|
+
if (childValue !== undefined) {
|
|
1369
|
+
groupDefaults[childField.key] = childValue;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return groupDefaults;
|
|
1374
|
+
}
|
|
1375
|
+
const valueField = field;
|
|
1376
|
+
const isNullable = valueField.nullable === true;
|
|
1377
|
+
if ('value' in field) {
|
|
1378
|
+
if (valueField.value !== null && valueField.value !== undefined) {
|
|
1379
|
+
return valueField.value;
|
|
1380
|
+
}
|
|
1381
|
+
if (valueField.value === null && isNullable) {
|
|
1382
|
+
return null;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
if (isNullable) {
|
|
1386
|
+
return null;
|
|
1387
|
+
}
|
|
1388
|
+
if (field.type === 'checkbox') {
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1391
|
+
if (field.type === 'array') {
|
|
1392
|
+
const arrayField = field;
|
|
1393
|
+
const itemDefinitions = arrayField.fields;
|
|
1394
|
+
if (!itemDefinitions || itemDefinitions.length === 0) {
|
|
1395
|
+
return [];
|
|
1396
|
+
}
|
|
1397
|
+
return itemDefinitions.map((itemDef) => {
|
|
1398
|
+
if (!Array.isArray(itemDef)) {
|
|
1399
|
+
return getFieldDefaultValue(itemDef, registry);
|
|
1400
|
+
}
|
|
1401
|
+
const itemFields = itemDef;
|
|
1402
|
+
let itemValue = {};
|
|
1403
|
+
for (const templateField of itemFields) {
|
|
1404
|
+
const fieldValue = getFieldDefaultValue(templateField, registry);
|
|
1405
|
+
const fieldValueHandling = getFieldValueHandling(templateField.type, registry);
|
|
1406
|
+
if (templateField.type === 'group' && 'key' in templateField && templateField.key) {
|
|
1407
|
+
itemValue[templateField.key] = fieldValue;
|
|
1408
|
+
}
|
|
1409
|
+
else if (templateField.type === 'row' || templateField.type === 'container') {
|
|
1410
|
+
if (fieldValue && typeof fieldValue === 'object') {
|
|
1411
|
+
itemValue = { ...itemValue, ...fieldValue };
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
else if (fieldValueHandling === 'include' && 'key' in templateField && templateField.key) {
|
|
1415
|
+
itemValue[templateField.key] = fieldValue;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return itemValue;
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
if (field.type === 'input' && 'props' in field) {
|
|
1422
|
+
const props = field.props;
|
|
1423
|
+
if (props?.type === 'number') {
|
|
1424
|
+
return NaN;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
return '';
|
|
1428
|
+
}
|
|
1429
|
+
|
|
2122
1430
|
function createContainerFieldProcessors() {
|
|
2123
1431
|
const flattenCache = new WeakMap();
|
|
2124
1432
|
const memoizedFlattenFields = (fields, registry) => {
|
|
@@ -2553,60 +1861,6 @@ function isValidFormConfiguration(fields) {
|
|
|
2553
1861
|
return FormModeValidator.validateFormConfiguration(fields).isValid;
|
|
2554
1862
|
}
|
|
2555
1863
|
|
|
2556
|
-
const COMPONENT_CACHE = new InjectionToken('COMPONENT_CACHE', {
|
|
2557
|
-
providedIn: 'root',
|
|
2558
|
-
factory: () => new Map(),
|
|
2559
|
-
});
|
|
2560
|
-
function injectFieldRegistry() {
|
|
2561
|
-
const registry = inject(FIELD_REGISTRY);
|
|
2562
|
-
const componentCache = inject(COMPONENT_CACHE);
|
|
2563
|
-
return {
|
|
2564
|
-
getType(name) {
|
|
2565
|
-
return registry.get(name);
|
|
2566
|
-
},
|
|
2567
|
-
hasType(name) {
|
|
2568
|
-
return registry.has(name);
|
|
2569
|
-
},
|
|
2570
|
-
async loadTypeComponent(name) {
|
|
2571
|
-
const fieldType = registry.get(name);
|
|
2572
|
-
if (!fieldType) {
|
|
2573
|
-
throw new DynamicFormError(`Field type "${name}" is not registered`);
|
|
2574
|
-
}
|
|
2575
|
-
if (!fieldType.loadComponent) {
|
|
2576
|
-
return undefined;
|
|
2577
|
-
}
|
|
2578
|
-
const cached = componentCache.get(name);
|
|
2579
|
-
if (cached) {
|
|
2580
|
-
return cached;
|
|
2581
|
-
}
|
|
2582
|
-
try {
|
|
2583
|
-
const component = resolveDefaultExport(await fieldType.loadComponent());
|
|
2584
|
-
if (component) {
|
|
2585
|
-
componentCache.set(name, component);
|
|
2586
|
-
}
|
|
2587
|
-
return component;
|
|
2588
|
-
}
|
|
2589
|
-
catch (error) {
|
|
2590
|
-
throw new DynamicFormError(`Failed to load component for field type "${name}": ${error}`);
|
|
2591
|
-
}
|
|
2592
|
-
},
|
|
2593
|
-
getLoadedComponent(name) {
|
|
2594
|
-
return componentCache.get(name);
|
|
2595
|
-
},
|
|
2596
|
-
getTypes() {
|
|
2597
|
-
return Array.from(registry.values());
|
|
2598
|
-
},
|
|
2599
|
-
registerTypes(types) {
|
|
2600
|
-
types.forEach((type) => {
|
|
2601
|
-
registry.set(type.name, type);
|
|
2602
|
-
});
|
|
2603
|
-
},
|
|
2604
|
-
get raw() {
|
|
2605
|
-
return registry;
|
|
2606
|
-
},
|
|
2607
|
-
};
|
|
2608
|
-
}
|
|
2609
|
-
|
|
2610
1864
|
function collectFieldData(fields, data, registry, pathPrefix, collectKeys = true) {
|
|
2611
1865
|
for (const field of fields) {
|
|
2612
1866
|
const valueHandling = field.type ? getFieldValueHandling(field.type, registry) : 'include';
|
|
@@ -2653,7 +1907,9 @@ function validateRegexPattern(pattern, fieldKey, errors) {
|
|
|
2653
1907
|
new RegExp(pattern);
|
|
2654
1908
|
}
|
|
2655
1909
|
catch (e) {
|
|
2656
|
-
|
|
1910
|
+
errors.push(DEV_MODE
|
|
1911
|
+
? `Invalid regex pattern in validator for field '${fieldKey}': '${pattern}' — ${e instanceof Error ? e.message : String(e)}`
|
|
1912
|
+
: `Invalid regex pattern: '${pattern}'`);
|
|
2657
1913
|
}
|
|
2658
1914
|
}
|
|
2659
1915
|
function validateNoDuplicateKeys(allKeys) {
|
|
@@ -2669,7 +1925,9 @@ function validateNoDuplicateKeys(allKeys) {
|
|
|
2669
1925
|
const duplicateList = Array.from(duplicates)
|
|
2670
1926
|
.map((k) => `'${k}'`)
|
|
2671
1927
|
.join(', ');
|
|
2672
|
-
|
|
1928
|
+
throw new DynamicFormError(DEV_MODE
|
|
1929
|
+
? `Duplicate field keys detected: ${duplicateList}. Field keys must be unique within their group scope (the same key may appear inside different groups).`
|
|
1930
|
+
: `Duplicate field keys detected: ${duplicateList}.`);
|
|
2673
1931
|
}
|
|
2674
1932
|
}
|
|
2675
1933
|
function validateNoDomIdCollisions(domIds) {
|
|
@@ -2685,9 +1943,11 @@ function validateNoDomIdCollisions(domIds) {
|
|
|
2685
1943
|
const duplicateList = Array.from(duplicates)
|
|
2686
1944
|
.map((k) => `'${k}'`)
|
|
2687
1945
|
.join(', ');
|
|
2688
|
-
|
|
2689
|
-
`
|
|
2690
|
-
|
|
1946
|
+
throw new DynamicFormError(DEV_MODE
|
|
1947
|
+
? `DOM id collision detected for: ${duplicateList}. Group-nested keys are joined with '_' to form DOM ids ` +
|
|
1948
|
+
`(e.g. group 'foo' + leaf 'bar' renders as id='foo_bar'). A separate top-level key like 'foo_bar' or a different ` +
|
|
1949
|
+
`group/leaf pair that underscores to the same string will produce duplicate ids. Rename one of the colliding keys.`
|
|
1950
|
+
: `DOM id collision detected for: ${duplicateList}.`);
|
|
2691
1951
|
}
|
|
2692
1952
|
}
|
|
2693
1953
|
function validateFieldTypesRegistered(allTypes, registry, logger) {
|
|
@@ -2717,12 +1977,15 @@ function validateFormConfig(fields, registry, logger) {
|
|
|
2717
1977
|
const list = Array.from(new Set(data.dottedKeys))
|
|
2718
1978
|
.map((k) => `'${k}'`)
|
|
2719
1979
|
.join(', ');
|
|
2720
|
-
|
|
2721
|
-
`(
|
|
1980
|
+
throw new DynamicFormError(DEV_MODE
|
|
1981
|
+
? `Field key(s) contain a dot: ${list}. Dotted keys are not supported — express nesting with 'group' fields ` +
|
|
1982
|
+
`(e.g. { type: 'group', key: 'address', fields: [{ type: 'input', key: 'city' }] }).`
|
|
1983
|
+
: `Field key(s) contain a dot: ${list}.`);
|
|
2722
1984
|
}
|
|
2723
1985
|
validateNoDuplicateKeys(data.keys);
|
|
2724
1986
|
validateNoDomIdCollisions(data.domIds);
|
|
2725
|
-
|
|
1987
|
+
if (DEV_MODE)
|
|
1988
|
+
validateFieldTypesRegistered(data.types, registry, logger);
|
|
2726
1989
|
if (data.regexErrors.length > 0) {
|
|
2727
1990
|
throw new DynamicFormError(data.regexErrors.length === 1
|
|
2728
1991
|
? data.regexErrors[0]
|
|
@@ -2937,6 +2200,33 @@ function resolveExclusionConfig(global, form, field) {
|
|
|
2937
2200
|
excludeValueIfReadonly: field?.excludeValueIfReadonly ?? form?.excludeValueIfReadonly ?? global.excludeValueIfReadonly,
|
|
2938
2201
|
};
|
|
2939
2202
|
}
|
|
2203
|
+
function hasEnabledValueExclusion(fields, global, form) {
|
|
2204
|
+
return fields.some((field) => {
|
|
2205
|
+
const resolved = resolveExclusionConfig(global, form, field);
|
|
2206
|
+
if (resolved.excludeValueIfHidden || resolved.excludeValueIfDisabled || resolved.excludeValueIfReadonly) {
|
|
2207
|
+
return true;
|
|
2208
|
+
}
|
|
2209
|
+
return (isGroupField(field) && !!field.fields && hasEnabledValueExclusion(Object.values(field.fields), global, form));
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
function filterDeclaredValueShape(rawValue, fields, registry) {
|
|
2213
|
+
const kept = {};
|
|
2214
|
+
for (const field of fields) {
|
|
2215
|
+
const key = field.key;
|
|
2216
|
+
if (!key || !(key in rawValue))
|
|
2217
|
+
continue;
|
|
2218
|
+
const valueHandling = getFieldValueHandling(field.type, registry);
|
|
2219
|
+
if (valueHandling === 'exclude' || valueHandling === 'flatten')
|
|
2220
|
+
continue;
|
|
2221
|
+
const value = rawValue[key];
|
|
2222
|
+
if (isGroupField(field) && field.fields && value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
2223
|
+
kept[key] = filterDeclaredValueShape(value, Object.values(field.fields), registry);
|
|
2224
|
+
continue;
|
|
2225
|
+
}
|
|
2226
|
+
kept[key] = value;
|
|
2227
|
+
}
|
|
2228
|
+
return kept;
|
|
2229
|
+
}
|
|
2940
2230
|
function shouldExcludeField(field, fieldState, config) {
|
|
2941
2231
|
const state = fieldState();
|
|
2942
2232
|
const inherited = field.inheritedExclusionState;
|
|
@@ -2954,7 +2244,10 @@ function shouldExcludeField(field, fieldState, config) {
|
|
|
2954
2244
|
}
|
|
2955
2245
|
return false;
|
|
2956
2246
|
}
|
|
2957
|
-
function filterFormValue(rawValue, schemaFields, formTree, registry, globalDefaults, formOptions) {
|
|
2247
|
+
function filterFormValue(rawValue, schemaFields, formTree, registry, globalDefaults, formOptions, exclusionEnabled = hasEnabledValueExclusion(schemaFields, globalDefaults, formOptions)) {
|
|
2248
|
+
if (!exclusionEnabled) {
|
|
2249
|
+
return filterDeclaredValueShape(rawValue, schemaFields, registry);
|
|
2250
|
+
}
|
|
2958
2251
|
const result = {};
|
|
2959
2252
|
for (const field of schemaFields) {
|
|
2960
2253
|
const key = field.key;
|
|
@@ -2990,7 +2283,7 @@ function filterFormValue(rawValue, schemaFields, formTree, registry, globalDefau
|
|
|
2990
2283
|
if (groupValue && typeof groupValue === 'object') {
|
|
2991
2284
|
const childFields = Object.values(field.fields);
|
|
2992
2285
|
const nestedTree = fieldState;
|
|
2993
|
-
result[key] = filterFormValue(groupValue, childFields, nestedTree, registry, globalDefaults, formOptions);
|
|
2286
|
+
result[key] = filterFormValue(groupValue, childFields, nestedTree, registry, globalDefaults, formOptions, true);
|
|
2994
2287
|
}
|
|
2995
2288
|
else {
|
|
2996
2289
|
result[key] = groupValue;
|
|
@@ -3303,7 +2596,7 @@ class FormStateMachine {
|
|
|
3303
2596
|
if (!isTransitioningState(state) || state.phase !== Phase.Restoring) {
|
|
3304
2597
|
return { state, sideEffects: [] };
|
|
3305
2598
|
}
|
|
3306
|
-
|
|
2599
|
+
if (DEV_MODE && !state.pendingFormSetup) {
|
|
3307
2600
|
this.config.logger.warn('handleRestoreComplete: pendingFormSetup was not set — falling back to recomputing. ' +
|
|
3308
2601
|
'This indicates a bug in the transition flow.');
|
|
3309
2602
|
}
|
|
@@ -3366,7 +2659,7 @@ class FormStateMachine {
|
|
|
3366
2659
|
const state = untracked(() => this._state());
|
|
3367
2660
|
if (!isTransitioningState(state) || state.phase !== Phase.Restoring)
|
|
3368
2661
|
return;
|
|
3369
|
-
|
|
2662
|
+
if (DEV_MODE && !state.pendingFormSetup) {
|
|
3370
2663
|
this.config.logger.warn('RestoreValues effect: pendingFormSetup was not set — falling back to recomputing.');
|
|
3371
2664
|
}
|
|
3372
2665
|
const formSetup = state.pendingFormSetup ?? this.config.createFormSetup(state.pendingConfig);
|
|
@@ -3508,13 +2801,6 @@ class FormStateManager {
|
|
|
3508
2801
|
return detectFormMode(config.fields || []);
|
|
3509
2802
|
}, /* @ts-ignore */
|
|
3510
2803
|
...(ngDevMode ? [{ debugName: "formModeDetection" }] : /* istanbul ignore next */ []));
|
|
3511
|
-
formConfigValidation = computed(() => {
|
|
3512
|
-
const config = this.activeConfig();
|
|
3513
|
-
if (!config)
|
|
3514
|
-
return { isValid: true, errors: [], warnings: [] };
|
|
3515
|
-
return FormModeValidator.validateFormConfiguration(config.fields || []);
|
|
3516
|
-
}, /* @ts-ignore */
|
|
3517
|
-
...(ngDevMode ? [{ debugName: "formConfigValidation" }] : /* istanbul ignore next */ []));
|
|
3518
2804
|
effectiveFormOptions = computed(() => {
|
|
3519
2805
|
const config = this.activeConfig();
|
|
3520
2806
|
const configOptions = config?.options || {};
|
|
@@ -3522,6 +2808,8 @@ class FormStateManager {
|
|
|
3522
2808
|
return { ...configOptions, ...inputOptions };
|
|
3523
2809
|
}, /* @ts-ignore */
|
|
3524
2810
|
...(ngDevMode ? [{ debugName: "effectiveFormOptions" }] : /* istanbul ignore next */ []));
|
|
2811
|
+
pendingPageRequest = signal(null, /* @ts-ignore */
|
|
2812
|
+
...(ngDevMode ? [{ debugName: "pendingPageRequest" }] : /* istanbul ignore next */ []));
|
|
3525
2813
|
pageFieldDefinitions = computed(() => {
|
|
3526
2814
|
const config = this.activeConfig();
|
|
3527
2815
|
const mode = this.formModeDetection().mode;
|
|
@@ -3559,21 +2847,70 @@ class FormStateManager {
|
|
|
3559
2847
|
return this.createEmptyFormSetup(registry);
|
|
3560
2848
|
}, /* @ts-ignore */
|
|
3561
2849
|
...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
|
|
2850
|
+
formSetupConfig = computed(() => {
|
|
2851
|
+
const state = this.machine.state();
|
|
2852
|
+
if (isReadyState(state))
|
|
2853
|
+
return state.config;
|
|
2854
|
+
if (isTransitioningState(state)) {
|
|
2855
|
+
if (state.phase === Phase.Restoring && state.pendingFormSetup) {
|
|
2856
|
+
return state.pendingConfig;
|
|
2857
|
+
}
|
|
2858
|
+
return state.currentConfig;
|
|
2859
|
+
}
|
|
2860
|
+
if (state.type === LifecycleState.Initializing)
|
|
2861
|
+
return state.config;
|
|
2862
|
+
if (state.type === LifecycleState.Uninitialized)
|
|
2863
|
+
return this.activeConfig();
|
|
2864
|
+
return undefined;
|
|
2865
|
+
}, /* @ts-ignore */
|
|
2866
|
+
...(ngDevMode ? [{ debugName: "formSetupConfig" }] : /* istanbul ignore next */ []));
|
|
2867
|
+
activePageState = linkedSignal({ ...(ngDevMode ? { debugName: "activePageState" } : /* istanbul ignore next */ {}), source: this.activeConfig,
|
|
2868
|
+
computation: (config) => ({ config, initialized: false, index: 0 }) });
|
|
3562
2869
|
defaultValues = linkedSignal(() => this.formSetup().defaultValues, /* @ts-ignore */
|
|
3563
2870
|
...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
|
|
3564
2871
|
validKeys = computed(() => {
|
|
3565
2872
|
const schemaFields = this.formSetup().schemaFields;
|
|
3566
2873
|
if (!schemaFields || schemaFields.length === 0)
|
|
3567
2874
|
return undefined;
|
|
3568
|
-
|
|
2875
|
+
const keys = new Set();
|
|
2876
|
+
for (const field of schemaFields) {
|
|
2877
|
+
if (field.key === undefined)
|
|
2878
|
+
continue;
|
|
2879
|
+
keys.add(field.key);
|
|
2880
|
+
}
|
|
2881
|
+
return keys;
|
|
3569
2882
|
}, /* @ts-ignore */
|
|
3570
2883
|
...(ngDevMode ? [{ debugName: "validKeys" }] : /* istanbul ignore next */ []));
|
|
3571
|
-
|
|
2884
|
+
boundValueExclusionEnabled = computed(() => {
|
|
2885
|
+
const setup = this.formSetup();
|
|
2886
|
+
if (!setup.schemaFields || setup.schemaFields.length === 0)
|
|
2887
|
+
return false;
|
|
2888
|
+
const options = this.effectiveFormOptions();
|
|
2889
|
+
return hasEnabledValueExclusion(setup.schemaFields, BOUND_VALUE_EXCLUSION_BASELINE, {
|
|
2890
|
+
excludeValueIfHidden: options.excludeValueIfHidden,
|
|
2891
|
+
excludeValueIfDisabled: options.excludeValueIfDisabled,
|
|
2892
|
+
excludeValueIfReadonly: options.excludeValueIfReadonly,
|
|
2893
|
+
});
|
|
2894
|
+
}, /* @ts-ignore */
|
|
2895
|
+
...(ngDevMode ? [{ debugName: "boundValueExclusionEnabled" }] : /* istanbul ignore next */ []));
|
|
2896
|
+
valueExclusionEnabled = computed(() => {
|
|
3572
2897
|
const setup = this.formSetup();
|
|
3573
2898
|
if (!setup.schemaFields || setup.schemaFields.length === 0)
|
|
2899
|
+
return false;
|
|
2900
|
+
const options = this.effectiveFormOptions();
|
|
2901
|
+
return hasEnabledValueExclusion(setup.schemaFields, this.valueExclusionDefaults, {
|
|
2902
|
+
excludeValueIfHidden: options.excludeValueIfHidden,
|
|
2903
|
+
excludeValueIfDisabled: options.excludeValueIfDisabled,
|
|
2904
|
+
excludeValueIfReadonly: options.excludeValueIfReadonly,
|
|
2905
|
+
});
|
|
2906
|
+
}, /* @ts-ignore */
|
|
2907
|
+
...(ngDevMode ? [{ debugName: "valueExclusionEnabled" }] : /* istanbul ignore next */ []));
|
|
2908
|
+
fieldStateSnapshot = computed(() => {
|
|
2909
|
+
const schemaFields = this.formSetup().schemaFields;
|
|
2910
|
+
if (!this.formSchemaReady() || !this.boundValueExclusionEnabled() || !schemaFields || schemaFields.length === 0)
|
|
3574
2911
|
return {};
|
|
3575
2912
|
const snapshot = {};
|
|
3576
|
-
collectFieldStateSnapshot(
|
|
2913
|
+
collectFieldStateSnapshot(schemaFields, asFieldTreeRecord(this.form()), [], snapshot);
|
|
3577
2914
|
return snapshot;
|
|
3578
2915
|
}, /* @ts-ignore */
|
|
3579
2916
|
...(ngDevMode ? [{ debugName: "fieldStateSnapshot" }] : /* istanbul ignore next */ []));
|
|
@@ -3581,8 +2918,22 @@ class FormStateManager {
|
|
|
3581
2918
|
...(ngDevMode ? [{ debugName: "excludedValueStore" }] : /* istanbul ignore next */ []));
|
|
3582
2919
|
prevFieldStateSnapshot = signal({}, /* @ts-ignore */
|
|
3583
2920
|
...(ngDevMode ? [{ debugName: "prevFieldStateSnapshot" }] : /* istanbul ignore next */ []));
|
|
2921
|
+
lastOutwardValue = { current: undefined, pending: false };
|
|
2922
|
+
lastExternalValue = { current: undefined };
|
|
2923
|
+
externalValue = computed(() => {
|
|
2924
|
+
const incoming = this.deps.value();
|
|
2925
|
+
if (this.lastOutwardValue.pending && incoming === this.lastOutwardValue.current) {
|
|
2926
|
+
this.lastOutwardValue.pending = false;
|
|
2927
|
+
return this.lastExternalValue.current;
|
|
2928
|
+
}
|
|
2929
|
+
this.lastOutwardValue.pending = false;
|
|
2930
|
+
this.lastExternalValue.current = incoming;
|
|
2931
|
+
return incoming;
|
|
2932
|
+
}, /* @ts-ignore */
|
|
2933
|
+
...(ngDevMode ? [{ debugName: "externalValue" }] : /* istanbul ignore next */ []));
|
|
3584
2934
|
entity = linkedSignal(() => {
|
|
3585
|
-
|
|
2935
|
+
this.externalValue();
|
|
2936
|
+
const inputValue = untracked(() => this.deps.value());
|
|
3586
2937
|
const defaults = this.defaultValues();
|
|
3587
2938
|
const keys = this.validKeys();
|
|
3588
2939
|
const saved = this.excludedValueStore();
|
|
@@ -3602,24 +2953,62 @@ class FormStateManager {
|
|
|
3602
2953
|
debugName: 'FormStateManager.entity',
|
|
3603
2954
|
equal: isEqual,
|
|
3604
2955
|
});
|
|
3605
|
-
|
|
2956
|
+
loadFormSchemaService = injectAsync(() => import('./ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs'));
|
|
2957
|
+
formSchemaRequest = computed(() => {
|
|
3606
2958
|
const setup = this.formSetup();
|
|
3607
2959
|
const config = this.activeConfig();
|
|
3608
|
-
if (!config)
|
|
2960
|
+
if (!config || (!(setup.schemaFields?.length > 0) && !config.schema))
|
|
2961
|
+
return undefined;
|
|
2962
|
+
return {
|
|
2963
|
+
fields: setup.schemaFields ?? [],
|
|
2964
|
+
registry: setup.registry,
|
|
2965
|
+
formLevelSchema: config.schema,
|
|
2966
|
+
validateWhenHidden: this.effectiveFormOptions().validateWhenHidden,
|
|
2967
|
+
};
|
|
2968
|
+
}, /* @ts-ignore */
|
|
2969
|
+
...(ngDevMode ? [{ debugName: "formSchemaRequest" }] : /* istanbul ignore next */ []));
|
|
2970
|
+
formSchemaResource = resource({ ...(ngDevMode ? { debugName: "formSchemaResource" } : /* istanbul ignore next */ {}), params: () => this.formSchemaRequest(),
|
|
2971
|
+
loader: async ({ params }) => {
|
|
2972
|
+
const schemaService = await this.loadFormSchemaService();
|
|
2973
|
+
return runInInjectionContext(this.injector, () => schemaService.create(params));
|
|
2974
|
+
} });
|
|
2975
|
+
schemaCache = { current: undefined };
|
|
2976
|
+
trackSchemaCache = computed(() => {
|
|
2977
|
+
const request = this.formSchemaRequest();
|
|
2978
|
+
if (!request) {
|
|
2979
|
+
this.schemaCache.current = undefined;
|
|
3609
2980
|
return undefined;
|
|
3610
|
-
if (setup.schemaFields?.length) {
|
|
3611
|
-
const crossFieldCollection = collectCrossFieldEntries(setup.schemaFields);
|
|
3612
|
-
return createSchemaFromFields(setup.schemaFields, setup.registry, {
|
|
3613
|
-
crossFieldValidators: crossFieldCollection.validators,
|
|
3614
|
-
formLevelSchema: config.schema,
|
|
3615
|
-
validateWhenHidden: this.effectiveFormOptions().validateWhenHidden,
|
|
3616
|
-
});
|
|
3617
2981
|
}
|
|
3618
|
-
if (
|
|
3619
|
-
|
|
2982
|
+
if (this.formSchemaResource.status() === 'error') {
|
|
2983
|
+
this.schemaCache.current = undefined;
|
|
2984
|
+
return undefined;
|
|
3620
2985
|
}
|
|
3621
|
-
|
|
3622
|
-
|
|
2986
|
+
if (this.formSchemaResource.hasValue()) {
|
|
2987
|
+
this.schemaCache.current = { schema: this.formSchemaResource.value(), request };
|
|
2988
|
+
}
|
|
2989
|
+
return this.schemaCache.current;
|
|
2990
|
+
}, /* @ts-ignore */
|
|
2991
|
+
...(ngDevMode ? [{ debugName: "trackSchemaCache" }] : /* istanbul ignore next */ []));
|
|
2992
|
+
isSchemaCurrent = computed(() => {
|
|
2993
|
+
if (this.formSetupConfig() !== this.activeConfig())
|
|
2994
|
+
return false;
|
|
2995
|
+
const request = this.formSchemaRequest();
|
|
2996
|
+
if (!request)
|
|
2997
|
+
return true;
|
|
2998
|
+
return this.trackSchemaCache()?.request === request;
|
|
2999
|
+
}, /* @ts-ignore */
|
|
3000
|
+
...(ngDevMode ? [{ debugName: "isSchemaCurrent" }] : /* istanbul ignore next */ []));
|
|
3001
|
+
formSchemaReady = computed(() => {
|
|
3002
|
+
if (this.formSetupConfig() !== this.activeConfig())
|
|
3003
|
+
return false;
|
|
3004
|
+
if (!this.formSchemaRequest())
|
|
3005
|
+
return true;
|
|
3006
|
+
if (this.formSchemaResource.status() === 'error')
|
|
3007
|
+
return true;
|
|
3008
|
+
return this.trackSchemaCache() !== undefined;
|
|
3009
|
+
}, /* @ts-ignore */
|
|
3010
|
+
...(ngDevMode ? [{ debugName: "formSchemaReady" }] : /* istanbul ignore next */ []));
|
|
3011
|
+
formSchema = computed(() => this.trackSchemaCache()?.schema, /* @ts-ignore */
|
|
3623
3012
|
...(ngDevMode ? [{ debugName: "formSchema" }] : /* istanbul ignore next */ []));
|
|
3624
3013
|
form = computed(() => {
|
|
3625
3014
|
const schema = this.formSchema();
|
|
@@ -3650,13 +3039,16 @@ class FormStateManager {
|
|
|
3650
3039
|
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
3651
3040
|
formInstance = computed(() => this.form()(), /* @ts-ignore */
|
|
3652
3041
|
...(ngDevMode ? [{ debugName: "formInstance" }] : /* istanbul ignore next */ []));
|
|
3653
|
-
|
|
3042
|
+
modelValue = computed(() => this.formInstance().value(), /* @ts-ignore */
|
|
3043
|
+
...(ngDevMode ? [{ debugName: "modelValue" }] : /* istanbul ignore next */ []));
|
|
3044
|
+
formValue = computed(() => this.modelValue(), /* @ts-ignore */
|
|
3654
3045
|
...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
|
|
3655
3046
|
filteredFormValue = computed(() => {
|
|
3656
|
-
const rawValue = this.
|
|
3047
|
+
const rawValue = this.modelValue();
|
|
3657
3048
|
const setup = this.formSetup();
|
|
3049
|
+
const schemaFields = setup.schemaFields;
|
|
3658
3050
|
const options = this.effectiveFormOptions();
|
|
3659
|
-
if (!
|
|
3051
|
+
if (!this.formSchemaReady() || !schemaFields || schemaFields.length === 0) {
|
|
3660
3052
|
return rawValue;
|
|
3661
3053
|
}
|
|
3662
3054
|
const formTree = this.form();
|
|
@@ -3666,14 +3058,15 @@ class FormStateManager {
|
|
|
3666
3058
|
excludeValueIfReadonly: options.excludeValueIfReadonly,
|
|
3667
3059
|
};
|
|
3668
3060
|
const fieldTreeRecord = asFieldTreeRecord(formTree);
|
|
3669
|
-
return filterFormValue(rawValue,
|
|
3061
|
+
return filterFormValue(rawValue, schemaFields, fieldTreeRecord, setup.registry, this.valueExclusionDefaults, formOptions, this.valueExclusionEnabled());
|
|
3670
3062
|
}, /* @ts-ignore */
|
|
3671
3063
|
...(ngDevMode ? [{ debugName: "filteredFormValue" }] : /* istanbul ignore next */ []));
|
|
3672
3064
|
boundFormValue = computed(() => {
|
|
3673
|
-
const rawValue = this.
|
|
3065
|
+
const rawValue = this.modelValue();
|
|
3674
3066
|
const setup = this.formSetup();
|
|
3067
|
+
const schemaFields = setup.schemaFields;
|
|
3675
3068
|
const options = this.effectiveFormOptions();
|
|
3676
|
-
if (!
|
|
3069
|
+
if (!this.formSchemaReady() || !schemaFields || schemaFields.length === 0) {
|
|
3677
3070
|
return rawValue;
|
|
3678
3071
|
}
|
|
3679
3072
|
const formTree = this.form();
|
|
@@ -3683,26 +3076,26 @@ class FormStateManager {
|
|
|
3683
3076
|
excludeValueIfReadonly: options.excludeValueIfReadonly,
|
|
3684
3077
|
};
|
|
3685
3078
|
const fieldTreeRecord = asFieldTreeRecord(formTree);
|
|
3686
|
-
return filterFormValue(rawValue,
|
|
3079
|
+
return filterFormValue(rawValue, schemaFields, fieldTreeRecord, setup.registry, BOUND_VALUE_EXCLUSION_BASELINE, formOptions, this.boundValueExclusionEnabled());
|
|
3687
3080
|
}, /* @ts-ignore */
|
|
3688
3081
|
...(ngDevMode ? [{ debugName: "boundFormValue" }] : /* istanbul ignore next */ []));
|
|
3689
|
-
|
|
3082
|
+
valid = computed(() => this.formSchemaReady() && this.formSchemaResource.status() !== 'error' && this.formInstance().valid(), /* @ts-ignore */
|
|
3690
3083
|
...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
|
|
3691
|
-
invalid = computed(() => this.formInstance().invalid(), /* @ts-ignore */
|
|
3084
|
+
invalid = computed(() => !this.formSchemaReady() || this.formSchemaResource.status() === 'error' || this.formInstance().invalid(), /* @ts-ignore */
|
|
3692
3085
|
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
3693
|
-
dirty = computed(() => this.formInstance().dirty(), /* @ts-ignore */
|
|
3086
|
+
dirty = computed(() => this.formSchemaReady() && this.formInstance().dirty(), /* @ts-ignore */
|
|
3694
3087
|
...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
|
|
3695
|
-
touched = computed(() => this.formInstance().touched(), /* @ts-ignore */
|
|
3088
|
+
touched = computed(() => this.formSchemaReady() && this.formInstance().touched(), /* @ts-ignore */
|
|
3696
3089
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
3697
|
-
errors = computed(() => this.formInstance().errors(), /* @ts-ignore */
|
|
3090
|
+
errors = computed(() => (this.formSchemaReady() ? this.formInstance().errors() : []), /* @ts-ignore */
|
|
3698
3091
|
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
3699
3092
|
disabled = computed(() => {
|
|
3700
3093
|
const optionsDisabled = this.effectiveFormOptions().disabled;
|
|
3701
|
-
const formDisabled = this.formInstance().disabled();
|
|
3094
|
+
const formDisabled = this.formSchemaReady() && this.formInstance().disabled();
|
|
3702
3095
|
return optionsDisabled ?? formDisabled;
|
|
3703
3096
|
}, /* @ts-ignore */
|
|
3704
3097
|
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
3705
|
-
submitting = computed(() => this.formInstance().submitting(), /* @ts-ignore */
|
|
3098
|
+
submitting = computed(() => this.formSchemaReady() && this.formInstance().submitting(), /* @ts-ignore */
|
|
3706
3099
|
...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
|
|
3707
3100
|
fieldsSource = computed(() => {
|
|
3708
3101
|
const state = this.machine.state();
|
|
@@ -3817,6 +3210,10 @@ class FormStateManager {
|
|
|
3817
3210
|
}), takeUntilDestroyed(this.destroyRef));
|
|
3818
3211
|
this.reset$ = this.eventBus.on('form-reset').pipe(map(() => undefined), takeUntilDestroyed(this.destroyRef));
|
|
3819
3212
|
this.cleared$ = this.eventBus.on('form-clear').pipe(map(() => undefined), takeUntilDestroyed(this.destroyRef));
|
|
3213
|
+
this.eventBus
|
|
3214
|
+
.on('go-to-page')
|
|
3215
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3216
|
+
.subscribe((event) => this.pendingPageRequest.set({ index: event.pageIndex, options: event.options }));
|
|
3820
3217
|
this.machine = createFormStateMachine({
|
|
3821
3218
|
injector: this.injector,
|
|
3822
3219
|
destroyRef: this.destroyRef,
|
|
@@ -3880,6 +3277,11 @@ class FormStateManager {
|
|
|
3880
3277
|
this.eventBus.dispatch(FormSubmitEvent);
|
|
3881
3278
|
}
|
|
3882
3279
|
setupEffects() {
|
|
3280
|
+
explicitEffect([this.formSchemaResource.error], ([error]) => {
|
|
3281
|
+
if (error) {
|
|
3282
|
+
this.logger.error('Failed to compile the form schema; rendering without validation and blocking submission.', error);
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3883
3285
|
explicitEffect([this.deps.config], ([config]) => {
|
|
3884
3286
|
this.fieldLoadingErrors.set([]);
|
|
3885
3287
|
const state = this.machine.currentState;
|
|
@@ -3931,17 +3333,24 @@ class FormStateManager {
|
|
|
3931
3333
|
explicitEffect([this.boundFormValue], ([currentBound]) => {
|
|
3932
3334
|
const currentValue = this.deps.value();
|
|
3933
3335
|
if (!isEqual(currentBound, currentValue)) {
|
|
3336
|
+
this.lastOutwardValue.current = currentBound;
|
|
3337
|
+
this.lastOutwardValue.pending = true;
|
|
3934
3338
|
this.deps.value.set(currentBound);
|
|
3935
3339
|
}
|
|
3936
3340
|
});
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3341
|
+
if (DEV_MODE) {
|
|
3342
|
+
explicitEffect([this.activeConfig], ([config]) => {
|
|
3343
|
+
if (!config)
|
|
3344
|
+
return;
|
|
3345
|
+
const validation = FormModeValidator.validateFormConfiguration(config.fields || []);
|
|
3346
|
+
if (!validation.isValid) {
|
|
3347
|
+
this.logger.error('Invalid form configuration:', validation.errors);
|
|
3348
|
+
}
|
|
3349
|
+
if (validation.warnings.length > 0) {
|
|
3350
|
+
this.logger.warn('Form configuration warnings:', validation.warnings);
|
|
3351
|
+
}
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3945
3354
|
}
|
|
3946
3355
|
setupEventHandlers() {
|
|
3947
3356
|
this.eventBus
|
|
@@ -3985,48 +3394,457 @@ class FormStateManager {
|
|
|
3985
3394
|
validateFormConfig(normalizedFields, registry, this.logger);
|
|
3986
3395
|
const source = this.deps.source?.() ?? 'inline';
|
|
3987
3396
|
const { fields: sanitizedFields, warnings: addonWarnings } = walkAndValidateAddons(normalizedFields, registry, this.addonKindRegistry, source);
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
this.
|
|
3397
|
+
if (DEV_MODE) {
|
|
3398
|
+
const nextKeys = new Set();
|
|
3399
|
+
for (const w of addonWarnings) {
|
|
3400
|
+
const key = addonWarningKey(w);
|
|
3401
|
+
nextKeys.add(key);
|
|
3402
|
+
if (!this.lastAddonWarningKeys.has(key)) {
|
|
3403
|
+
this.logger.warn(formatAddonWarning(w));
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
this.lastAddonWarningKeys = nextKeys;
|
|
3407
|
+
}
|
|
3408
|
+
const validatedFields = sanitizedFields;
|
|
3409
|
+
const flattenedFields = this.fieldProcessors.memoizedFlattenFields(validatedFields, registry);
|
|
3410
|
+
const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(validatedFields, registry);
|
|
3411
|
+
const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);
|
|
3412
|
+
const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);
|
|
3413
|
+
const fieldsToRender = mode === 'paged' ? [] : flattenedFieldsForRendering;
|
|
3414
|
+
return {
|
|
3415
|
+
fields: fieldsToRender,
|
|
3416
|
+
schemaFields: flattenedFields,
|
|
3417
|
+
originalFields: fields,
|
|
3418
|
+
defaultValues,
|
|
3419
|
+
mode,
|
|
3420
|
+
registry,
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
createEmptyFormSetup(registry) {
|
|
3424
|
+
return {
|
|
3425
|
+
fields: [],
|
|
3426
|
+
schemaFields: [],
|
|
3427
|
+
defaultValues: {},
|
|
3428
|
+
mode: 'non-paged',
|
|
3429
|
+
registry,
|
|
3430
|
+
};
|
|
3431
|
+
}
|
|
3432
|
+
ngOnDestroy() {
|
|
3433
|
+
this.machine.dispatch({ type: Action.Destroy });
|
|
3434
|
+
}
|
|
3435
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FormStateManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3436
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FormStateManager });
|
|
3437
|
+
}
|
|
3438
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FormStateManager, decorators: [{
|
|
3439
|
+
type: Injectable
|
|
3440
|
+
}], ctorParameters: () => [] });
|
|
3441
|
+
|
|
3442
|
+
class PageOrchestratorComponent {
|
|
3443
|
+
eventBus = inject(EventBus);
|
|
3444
|
+
fieldContextRegistry = inject(FieldContextRegistryService);
|
|
3445
|
+
functionRegistry = inject(FunctionRegistryService);
|
|
3446
|
+
formOptions = inject(FORM_OPTIONS, { optional: true });
|
|
3447
|
+
globalPreloadWindow = inject(PAGE_PRELOAD_WINDOW);
|
|
3448
|
+
stateManager = inject(FormStateManager, { optional: true });
|
|
3449
|
+
pendingPageRequest = computed(() => this.stateManager?.pendingPageRequest() ?? null, /* @ts-ignore */
|
|
3450
|
+
...(ngDevMode ? [{ debugName: "pendingPageRequest" }] : /* istanbul ignore next */ []));
|
|
3451
|
+
schemaCurrent = computed(() => this.stateManager?.isSchemaCurrent() ?? true, /* @ts-ignore */
|
|
3452
|
+
...(ngDevMode ? [{ debugName: "schemaCurrent" }] : /* istanbul ignore next */ []));
|
|
3453
|
+
configScope = computed(() => this.stateManager?.activeConfig(), /* @ts-ignore */
|
|
3454
|
+
...(ngDevMode ? [{ debugName: "configScope" }] : /* istanbul ignore next */ []));
|
|
3455
|
+
pageFields = input.required(/* @ts-ignore */
|
|
3456
|
+
...(ngDevMode ? [{ debugName: "pageFields" }] : /* istanbul ignore next */ []));
|
|
3457
|
+
form = input.required(/* @ts-ignore */
|
|
3458
|
+
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
3459
|
+
fieldSignalContext = input.required(/* @ts-ignore */
|
|
3460
|
+
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
3461
|
+
pageHiddenStates = computed(() => {
|
|
3462
|
+
const pages = this.pageFields();
|
|
3463
|
+
return pages.map((page) => this.evaluatePageHidden(page));
|
|
3464
|
+
}, /* @ts-ignore */
|
|
3465
|
+
...(ngDevMode ? [{ debugName: "pageHiddenStates" }] : /* istanbul ignore next */ []));
|
|
3466
|
+
visiblePageIndices = computed(() => {
|
|
3467
|
+
const hiddenStates = this.pageHiddenStates();
|
|
3468
|
+
return hiddenStates
|
|
3469
|
+
.map((hidden, index) => ({ index, hidden }))
|
|
3470
|
+
.filter((item) => !item.hidden)
|
|
3471
|
+
.map((item) => item.index);
|
|
3472
|
+
}, /* @ts-ignore */
|
|
3473
|
+
...(ngDevMode ? [{ debugName: "visiblePageIndices" }] : /* istanbul ignore next */ []));
|
|
3474
|
+
activePageOwnership = linkedSignal({ ...(ngDevMode ? { debugName: "activePageOwnership" } : /* istanbul ignore next */ {}), source: this.configScope,
|
|
3475
|
+
computation: (config, previous) => {
|
|
3476
|
+
const totalPages = this.pageFields().length;
|
|
3477
|
+
if (totalPages === 0)
|
|
3478
|
+
return { config, initialized: true, index: 0 };
|
|
3479
|
+
return untracked(() => {
|
|
3480
|
+
const owned = this.stateManager?.activePageState();
|
|
3481
|
+
const mayRestore = previous === undefined || previous.source === config;
|
|
3482
|
+
if (mayRestore &&
|
|
3483
|
+
owned !== undefined &&
|
|
3484
|
+
owned.config === config &&
|
|
3485
|
+
owned.initialized &&
|
|
3486
|
+
owned.index >= 0 &&
|
|
3487
|
+
owned.index < totalPages) {
|
|
3488
|
+
return { config, initialized: true, index: owned.index };
|
|
3489
|
+
}
|
|
3490
|
+
if (this.initialPage().validate && !this.schemaCurrent()) {
|
|
3491
|
+
return { config, initialized: false, index: 0 };
|
|
3492
|
+
}
|
|
3493
|
+
return { config, initialized: true, index: this.resolveInitialLanding() };
|
|
3494
|
+
});
|
|
3495
|
+
} });
|
|
3496
|
+
currentPageIndex = computed(() => this.activePageOwnership().index, /* @ts-ignore */
|
|
3497
|
+
...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
|
|
3498
|
+
setCurrentPageIndex(index) {
|
|
3499
|
+
this.activePageOwnership.update((ownership) => ({ ...ownership, initialized: true, index }));
|
|
3500
|
+
}
|
|
3501
|
+
resolveInitialLanding() {
|
|
3502
|
+
const { index, validate } = this.initialPage();
|
|
3503
|
+
const visible = this.visiblePageIndices();
|
|
3504
|
+
if (index === 0 || visible.length === 0)
|
|
3505
|
+
return 0;
|
|
3506
|
+
const target = visible.includes(index) ? index : this.findNearestVisiblePage(index, visible);
|
|
3507
|
+
if (target <= 0)
|
|
3508
|
+
return Math.max(target, 0);
|
|
3509
|
+
if (!validate)
|
|
3510
|
+
return target;
|
|
3511
|
+
const firstInvalid = visible.filter((i) => i < target).find((i) => !this.isPageValid(i));
|
|
3512
|
+
return firstInvalid ?? target;
|
|
3513
|
+
}
|
|
3514
|
+
initialPage = computed(() => {
|
|
3515
|
+
const raw = this.formOptions?.()?.initialPage;
|
|
3516
|
+
const totalPages = this.pageFields().length;
|
|
3517
|
+
const requested = typeof raw === 'number' ? raw : raw?.index;
|
|
3518
|
+
const validate = typeof raw === 'number' ? false : (raw?.validate ?? false);
|
|
3519
|
+
if (typeof requested !== 'number' || !Number.isFinite(requested) || requested < 0 || totalPages === 0) {
|
|
3520
|
+
return { index: 0, validate: false };
|
|
3521
|
+
}
|
|
3522
|
+
return { index: Math.min(Math.trunc(requested), totalPages - 1), validate };
|
|
3523
|
+
}, /* @ts-ignore */
|
|
3524
|
+
...(ngDevMode ? [{ debugName: "initialPage" }] : /* istanbul ignore next */ []));
|
|
3525
|
+
state = computed(() => {
|
|
3526
|
+
const currentIndex = this.currentPageIndex();
|
|
3527
|
+
const totalPages = this.pageFields().length;
|
|
3528
|
+
const visibleIndices = this.visiblePageIndices();
|
|
3529
|
+
const currentVisiblePosition = visibleIndices.indexOf(currentIndex);
|
|
3530
|
+
const isFirstVisiblePage = currentVisiblePosition === 0 || currentVisiblePosition === -1;
|
|
3531
|
+
const isLastVisiblePage = currentVisiblePosition >= visibleIndices.length - 1;
|
|
3532
|
+
return {
|
|
3533
|
+
currentPageIndex: currentIndex,
|
|
3534
|
+
totalPages,
|
|
3535
|
+
isFirstPage: isFirstVisiblePage,
|
|
3536
|
+
isLastPage: isLastVisiblePage,
|
|
3537
|
+
};
|
|
3538
|
+
}, /* @ts-ignore */
|
|
3539
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
3540
|
+
currentPageValid = computed(() => (this.stateManager?.isSchemaCurrent() ?? true) && this.isPageValid(this.currentPageIndex()), /* @ts-ignore */
|
|
3541
|
+
...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
|
|
3542
|
+
isPageValid(pageIndex) {
|
|
3543
|
+
const pages = this.pageFields();
|
|
3544
|
+
const form = this.form();
|
|
3545
|
+
if (pageIndex < 0 || pageIndex >= pages.length) {
|
|
3546
|
+
return true;
|
|
3547
|
+
}
|
|
3548
|
+
const leafKeys = collectLeafFieldKeys(pages[pageIndex].fields || []);
|
|
3549
|
+
for (const fieldKey of leafKeys) {
|
|
3550
|
+
const field = form[fieldKey];
|
|
3551
|
+
if (field && typeof field === 'function') {
|
|
3552
|
+
const fieldState = field();
|
|
3553
|
+
if (fieldState && typeof fieldState.valid === 'function' && !fieldState.valid()) {
|
|
3554
|
+
return false;
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
return true;
|
|
3559
|
+
}
|
|
3560
|
+
extendedFieldSignalContext = computed(() => ({
|
|
3561
|
+
...this.fieldSignalContext(),
|
|
3562
|
+
currentPageValid: this.currentPageValid,
|
|
3563
|
+
}), /* @ts-ignore */
|
|
3564
|
+
...(ngDevMode ? [{ debugName: "extendedFieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
3565
|
+
preloadWindow = computed(() => {
|
|
3566
|
+
const perForm = this.formOptions?.()?.pagePreloadWindow;
|
|
3567
|
+
return clampWindowSize(perForm, this.globalPreloadWindow);
|
|
3568
|
+
}, /* @ts-ignore */
|
|
3569
|
+
...(ngDevMode ? [{ debugName: "preloadWindow" }] : /* istanbul ignore next */ []));
|
|
3570
|
+
pageRenderStates = computed(() => {
|
|
3571
|
+
const activeIndex = this.currentPageIndex();
|
|
3572
|
+
const hiddenStates = this.pageHiddenStates();
|
|
3573
|
+
const preloadWindow = this.preloadWindow();
|
|
3574
|
+
return this.pageFields().map((field, index) => ({
|
|
3575
|
+
field,
|
|
3576
|
+
index,
|
|
3577
|
+
hidden: hiddenStates[index],
|
|
3578
|
+
active: index === activeIndex,
|
|
3579
|
+
preload: index !== activeIndex && Math.abs(index - activeIndex) <= preloadWindow,
|
|
3580
|
+
}));
|
|
3581
|
+
}, /* @ts-ignore */
|
|
3582
|
+
...(ngDevMode ? [{ debugName: "pageRenderStates" }] : /* istanbul ignore next */ []));
|
|
3583
|
+
constructor() {
|
|
3584
|
+
this.setupEventListeners();
|
|
3585
|
+
const preloader = injectFormComponentPreloader();
|
|
3586
|
+
explicitEffect([this.pageRenderStates], ([states]) => {
|
|
3587
|
+
preloader.preloadFields(states.filter((page) => !page.hidden && (page.active || page.preload)).map((page) => page.field));
|
|
3588
|
+
});
|
|
3589
|
+
explicitEffect([this.schemaCurrent, this.pageFields], ([schemaCurrent]) => {
|
|
3590
|
+
const ownership = this.activePageOwnership();
|
|
3591
|
+
if (!schemaCurrent || ownership.initialized)
|
|
3592
|
+
return;
|
|
3593
|
+
this.activePageOwnership.set({ ...ownership, initialized: true, index: this.resolveInitialLanding() });
|
|
3594
|
+
});
|
|
3595
|
+
explicitEffect([this.state, this.visiblePageIndices], ([state, visibleIndices]) => {
|
|
3596
|
+
const currentVisiblePosition = visibleIndices.indexOf(state.currentPageIndex);
|
|
3597
|
+
if (currentVisiblePosition === -1 && visibleIndices.length > 0) {
|
|
3598
|
+
const nearest = this.findNearestVisiblePage(state.currentPageIndex, visibleIndices);
|
|
3599
|
+
if (nearest !== -1) {
|
|
3600
|
+
this.executePageChange(nearest);
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
});
|
|
3604
|
+
}
|
|
3605
|
+
navigateToNextPage() {
|
|
3606
|
+
const disableWhenPageInvalid = this.formOptions?.()?.nextButton?.disableWhenPageInvalid ?? true;
|
|
3607
|
+
if (disableWhenPageInvalid && !this.currentPageValid()) {
|
|
3608
|
+
return {
|
|
3609
|
+
success: false,
|
|
3610
|
+
newPageIndex: this.state().currentPageIndex,
|
|
3611
|
+
error: 'Current page has invalid fields',
|
|
3612
|
+
};
|
|
3613
|
+
}
|
|
3614
|
+
const currentState = this.state();
|
|
3615
|
+
const visibleIndices = this.visiblePageIndices();
|
|
3616
|
+
if (currentState.isLastPage) {
|
|
3617
|
+
return {
|
|
3618
|
+
success: false,
|
|
3619
|
+
newPageIndex: currentState.currentPageIndex,
|
|
3620
|
+
error: 'Already on the last visible page',
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
const currentVisiblePosition = visibleIndices.indexOf(currentState.currentPageIndex);
|
|
3624
|
+
if (currentVisiblePosition === -1 || currentVisiblePosition >= visibleIndices.length - 1) {
|
|
3625
|
+
return {
|
|
3626
|
+
success: false,
|
|
3627
|
+
newPageIndex: currentState.currentPageIndex,
|
|
3628
|
+
error: 'No next visible page available',
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
const nextVisiblePageIndex = visibleIndices[currentVisiblePosition + 1];
|
|
3632
|
+
return this.executePageChange(nextVisiblePageIndex);
|
|
3633
|
+
}
|
|
3634
|
+
navigateToPreviousPage() {
|
|
3635
|
+
const currentState = this.state();
|
|
3636
|
+
const visibleIndices = this.visiblePageIndices();
|
|
3637
|
+
if (currentState.isFirstPage) {
|
|
3638
|
+
return {
|
|
3639
|
+
success: false,
|
|
3640
|
+
newPageIndex: currentState.currentPageIndex,
|
|
3641
|
+
error: 'Already on the first visible page',
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
const currentVisiblePosition = visibleIndices.indexOf(currentState.currentPageIndex);
|
|
3645
|
+
if (currentVisiblePosition <= 0) {
|
|
3646
|
+
return {
|
|
3647
|
+
success: false,
|
|
3648
|
+
newPageIndex: currentState.currentPageIndex,
|
|
3649
|
+
error: 'No previous visible page available',
|
|
3650
|
+
};
|
|
3651
|
+
}
|
|
3652
|
+
const prevVisiblePageIndex = visibleIndices[currentVisiblePosition - 1];
|
|
3653
|
+
return this.executePageChange(prevVisiblePageIndex);
|
|
3654
|
+
}
|
|
3655
|
+
navigateToPage(pageIndex, options) {
|
|
3656
|
+
const currentIndex = this.state().currentPageIndex;
|
|
3657
|
+
const visibleIndices = this.visiblePageIndices();
|
|
3658
|
+
if (pageIndex <= currentIndex || !visibleIndices.includes(pageIndex)) {
|
|
3659
|
+
return this.executePageChange(pageIndex);
|
|
3660
|
+
}
|
|
3661
|
+
if (options?.validate === false) {
|
|
3662
|
+
return this.executePageChange(pageIndex);
|
|
3663
|
+
}
|
|
3664
|
+
const disableWhenPageInvalid = this.formOptions?.()?.nextButton?.disableWhenPageInvalid ?? true;
|
|
3665
|
+
if (!disableWhenPageInvalid) {
|
|
3666
|
+
return this.executePageChange(pageIndex);
|
|
3667
|
+
}
|
|
3668
|
+
const crossedPages = visibleIndices.filter((index) => index >= currentIndex && index < pageIndex);
|
|
3669
|
+
const firstInvalidPage = crossedPages.find((index) => !this.isPageValid(index));
|
|
3670
|
+
if (firstInvalidPage === undefined) {
|
|
3671
|
+
return this.executePageChange(pageIndex);
|
|
3672
|
+
}
|
|
3673
|
+
this.executePageChange(firstInvalidPage);
|
|
3674
|
+
return {
|
|
3675
|
+
success: false,
|
|
3676
|
+
newPageIndex: firstInvalidPage,
|
|
3677
|
+
error: `Cannot navigate to page ${pageIndex}: page ${firstInvalidPage} has invalid fields`,
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
executePageChange(pageIndex) {
|
|
3681
|
+
const currentState = this.state();
|
|
3682
|
+
const totalPages = currentState.totalPages;
|
|
3683
|
+
if (pageIndex < 0 || pageIndex >= totalPages) {
|
|
3684
|
+
return {
|
|
3685
|
+
success: false,
|
|
3686
|
+
newPageIndex: currentState.currentPageIndex,
|
|
3687
|
+
error: `Invalid page index: ${pageIndex}. Valid range is 0 to ${totalPages - 1}`,
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3690
|
+
const visibleIndices = this.visiblePageIndices();
|
|
3691
|
+
if (!visibleIndices.includes(pageIndex)) {
|
|
3692
|
+
return {
|
|
3693
|
+
success: false,
|
|
3694
|
+
newPageIndex: currentState.currentPageIndex,
|
|
3695
|
+
error: `Cannot navigate to hidden page at index ${pageIndex}. Visible pages: [${visibleIndices.join(', ')}]`,
|
|
3696
|
+
};
|
|
3697
|
+
}
|
|
3698
|
+
if (pageIndex === currentState.currentPageIndex) {
|
|
3699
|
+
return {
|
|
3700
|
+
success: true,
|
|
3701
|
+
newPageIndex: pageIndex,
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
const previousIndex = currentState.currentPageIndex;
|
|
3705
|
+
this.setCurrentPageIndex(pageIndex);
|
|
3706
|
+
this.eventBus.dispatch(PageChangeEvent, pageIndex, totalPages, previousIndex);
|
|
3707
|
+
return {
|
|
3708
|
+
success: true,
|
|
3709
|
+
newPageIndex: pageIndex,
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
findNearestVisiblePage(currentIndex, visibleIndices) {
|
|
3713
|
+
let nearest = -1;
|
|
3714
|
+
let minDistance = Infinity;
|
|
3715
|
+
for (const idx of visibleIndices) {
|
|
3716
|
+
const distance = Math.abs(idx - currentIndex);
|
|
3717
|
+
if (distance < minDistance || (distance === minDistance && idx > nearest)) {
|
|
3718
|
+
minDistance = distance;
|
|
3719
|
+
nearest = idx;
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
return nearest;
|
|
3723
|
+
}
|
|
3724
|
+
setupEventListeners() {
|
|
3725
|
+
this.eventBus
|
|
3726
|
+
.on('next-page')
|
|
3727
|
+
.pipe(takeUntilDestroyed())
|
|
3728
|
+
.subscribe(() => {
|
|
3729
|
+
this.navigateToNextPage();
|
|
3730
|
+
});
|
|
3731
|
+
this.eventBus
|
|
3732
|
+
.on('previous-page')
|
|
3733
|
+
.pipe(takeUntilDestroyed())
|
|
3734
|
+
.subscribe(() => {
|
|
3735
|
+
this.navigateToPreviousPage();
|
|
3736
|
+
});
|
|
3737
|
+
explicitEffect([this.pendingPageRequest], ([request]) => {
|
|
3738
|
+
if (!request)
|
|
3739
|
+
return;
|
|
3740
|
+
this.stateManager?.pendingPageRequest.set(null);
|
|
3741
|
+
this.navigateToPage(request.index, request.options);
|
|
3742
|
+
});
|
|
3743
|
+
explicitEffect([this.state], ([state]) => this.eventBus.dispatch(PagerStateEvent, state));
|
|
3744
|
+
explicitEffect([this.activePageOwnership], ([ownership]) => this.stateManager?.activePageState.set(ownership));
|
|
3745
|
+
}
|
|
3746
|
+
evaluatePageHidden(page) {
|
|
3747
|
+
if (!page.logic || page.logic.length === 0) {
|
|
3748
|
+
return false;
|
|
3749
|
+
}
|
|
3750
|
+
const hiddenLogic = page.logic.filter((l) => l.type === 'hidden');
|
|
3751
|
+
if (hiddenLogic.length === 0) {
|
|
3752
|
+
return false;
|
|
3753
|
+
}
|
|
3754
|
+
for (const logic of hiddenLogic) {
|
|
3755
|
+
if (typeof logic.condition === 'boolean') {
|
|
3756
|
+
if (logic.condition) {
|
|
3757
|
+
return true;
|
|
3758
|
+
}
|
|
3759
|
+
continue;
|
|
3760
|
+
}
|
|
3761
|
+
const condition = logic.condition;
|
|
3762
|
+
const context = this.fieldContextRegistry.createDisplayOnlyContext(page.key || '', this.functionRegistry.getCustomFunctions());
|
|
3763
|
+
if (evaluateCondition(condition, context)) {
|
|
3764
|
+
return true;
|
|
3994
3765
|
}
|
|
3995
3766
|
}
|
|
3996
|
-
|
|
3997
|
-
const validatedFields = sanitizedFields;
|
|
3998
|
-
const flattenedFields = this.fieldProcessors.memoizedFlattenFields(validatedFields, registry);
|
|
3999
|
-
const flattenedFieldsForRendering = this.fieldProcessors.memoizedFlattenFieldsForRendering(validatedFields, registry);
|
|
4000
|
-
const fieldsById = this.fieldProcessors.memoizedKeyBy(flattenedFields);
|
|
4001
|
-
const defaultValues = this.fieldProcessors.memoizedDefaultValues(fieldsById, registry);
|
|
4002
|
-
const fieldsToRender = mode === 'paged' ? [] : flattenedFieldsForRendering;
|
|
4003
|
-
return {
|
|
4004
|
-
fields: fieldsToRender,
|
|
4005
|
-
schemaFields: flattenedFields,
|
|
4006
|
-
originalFields: fields,
|
|
4007
|
-
defaultValues,
|
|
4008
|
-
mode,
|
|
4009
|
-
registry,
|
|
4010
|
-
};
|
|
4011
|
-
}
|
|
4012
|
-
createEmptyFormSetup(registry) {
|
|
4013
|
-
return {
|
|
4014
|
-
fields: [],
|
|
4015
|
-
schemaFields: [],
|
|
4016
|
-
defaultValues: {},
|
|
4017
|
-
mode: 'non-paged',
|
|
4018
|
-
registry,
|
|
4019
|
-
};
|
|
3767
|
+
return false;
|
|
4020
3768
|
}
|
|
4021
|
-
|
|
4022
|
-
|
|
3769
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3770
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: PageOrchestratorComponent, isStandalone: true, selector: "div[page-orchestrator]", inputs: { pageFields: { classPropertyName: "pageFields", publicName: "pageFields", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, fieldSignalContext: { classPropertyName: "fieldSignalContext", publicName: "fieldSignalContext", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-current-page": "state().currentPageIndex", "attr.data-total-pages": "state().totalPages" }, classAttribute: "df-page-orchestrator" }, providers: [
|
|
3771
|
+
{
|
|
3772
|
+
provide: FIELD_SIGNAL_CONTEXT,
|
|
3773
|
+
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
3774
|
+
deps: [PageOrchestratorComponent],
|
|
3775
|
+
},
|
|
3776
|
+
], ngImport: i0, template: `
|
|
3777
|
+
@for (page of pageRenderStates(); track page.field.key) {
|
|
3778
|
+
<!--
|
|
3779
|
+
Skip pages hidden by 'hidden' logic conditions entirely. FormStateManager
|
|
3780
|
+
derives schema/value/validators from config (not from mounted components),
|
|
3781
|
+
so unmounting a hidden page has no effect on form state — only on render +
|
|
3782
|
+
CD cost. This is the page-level equivalent of the existing field-level
|
|
3783
|
+
\`@if (!field.hidden())\` gate in page-field.component.ts.
|
|
3784
|
+
-->
|
|
3785
|
+
@if (!page.hidden) {
|
|
3786
|
+
@if (page.active || page.preload) {
|
|
3787
|
+
@defer (when page.active; on idle) {
|
|
3788
|
+
<section page-field [field]="page.field" [key]="page.field.key" [pageIndex]="page.index" [isVisible]="page.active"></section>
|
|
3789
|
+
} @placeholder {
|
|
3790
|
+
<div class="df-page-placeholder" [attr.data-page-index]="page.index" [attr.data-page-key]="page.field.key"></div>
|
|
3791
|
+
}
|
|
3792
|
+
} @else {
|
|
3793
|
+
<!--
|
|
3794
|
+
Pages outside the preload window remain lightweight placeholders.
|
|
3795
|
+
Form state derives from config, so leaving their components unmounted
|
|
3796
|
+
does not affect validity, derivations, or navigation. The active page
|
|
3797
|
+
renders directly; configured neighbours preload declaratively on idle.
|
|
3798
|
+
-->
|
|
3799
|
+
<div class="df-page-placeholder" [attr.data-page-index]="page.index" [attr.data-page-key]="page.field.key"></div>
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
4023
3802
|
}
|
|
4024
|
-
|
|
4025
|
-
|
|
3803
|
+
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
|
|
3804
|
+
import('./ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs').then(m => m.default)]] });
|
|
4026
3805
|
}
|
|
4027
|
-
i0.ɵɵ
|
|
4028
|
-
|
|
4029
|
-
|
|
3806
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.1.5", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [/* @ts-ignore */
|
|
3807
|
+
import('./ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
3808
|
+
type: Component,
|
|
3809
|
+
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
3810
|
+
@for (page of pageRenderStates(); track page.field.key) {
|
|
3811
|
+
<!--
|
|
3812
|
+
Skip pages hidden by 'hidden' logic conditions entirely. FormStateManager
|
|
3813
|
+
derives schema/value/validators from config (not from mounted components),
|
|
3814
|
+
so unmounting a hidden page has no effect on form state — only on render +
|
|
3815
|
+
CD cost. This is the page-level equivalent of the existing field-level
|
|
3816
|
+
\`@if (!field.hidden())\` gate in page-field.component.ts.
|
|
3817
|
+
-->
|
|
3818
|
+
@if (!page.hidden) {
|
|
3819
|
+
@if (page.active || page.preload) {
|
|
3820
|
+
@defer (when page.active; on idle) {
|
|
3821
|
+
<section page-field [field]="page.field" [key]="page.field.key" [pageIndex]="page.index" [isVisible]="page.active"></section>
|
|
3822
|
+
} @placeholder {
|
|
3823
|
+
<div class="df-page-placeholder" [attr.data-page-index]="page.index" [attr.data-page-key]="page.field.key"></div>
|
|
3824
|
+
}
|
|
3825
|
+
} @else {
|
|
3826
|
+
<!--
|
|
3827
|
+
Pages outside the preload window remain lightweight placeholders.
|
|
3828
|
+
Form state derives from config, so leaving their components unmounted
|
|
3829
|
+
does not affect validity, derivations, or navigation. The active page
|
|
3830
|
+
renders directly; configured neighbours preload declaratively on idle.
|
|
3831
|
+
-->
|
|
3832
|
+
<div class="df-page-placeholder" [attr.data-page-index]="page.index" [attr.data-page-key]="page.field.key"></div>
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
`, host: {
|
|
3837
|
+
class: 'df-page-orchestrator',
|
|
3838
|
+
'[attr.data-current-page]': 'state().currentPageIndex',
|
|
3839
|
+
'[attr.data-total-pages]': 'state().totalPages',
|
|
3840
|
+
}, providers: [
|
|
3841
|
+
{
|
|
3842
|
+
provide: FIELD_SIGNAL_CONTEXT,
|
|
3843
|
+
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
3844
|
+
deps: [PageOrchestratorComponent],
|
|
3845
|
+
},
|
|
3846
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;position:relative;width:100%}\n"] }]
|
|
3847
|
+
}], ctorParameters: () => [], propDecorators: { pageFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageFields", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], fieldSignalContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldSignalContext", required: true }] }] } }) });
|
|
4030
3848
|
|
|
4031
3849
|
function traverseFieldsWithContext(fields, context, visitor, hooks) {
|
|
4032
3850
|
for (const field of fields) {
|
|
@@ -4105,14 +3923,14 @@ function createDerivationRenderGate() {
|
|
|
4105
3923
|
if (!has || engine() !== 'idle')
|
|
4106
3924
|
return;
|
|
4107
3925
|
engine.set('loading');
|
|
4108
|
-
pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-
|
|
3926
|
+
pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs')
|
|
4109
3927
|
.then(({ bootstrapDerivationOrchestrator }) => {
|
|
4110
3928
|
runInInjectionContext(injector, () => bootstrapDerivationOrchestrator());
|
|
4111
3929
|
engine.set('wired');
|
|
4112
3930
|
})
|
|
4113
3931
|
.catch((error) => {
|
|
4114
3932
|
engine.set('failed');
|
|
4115
|
-
logger.error('
|
|
3933
|
+
logger.error('Failed to load the derivation engine; derivations will not apply.', error);
|
|
4116
3934
|
}));
|
|
4117
3935
|
});
|
|
4118
3936
|
return computed(() => {
|
|
@@ -4123,6 +3941,68 @@ function createDerivationRenderGate() {
|
|
|
4123
3941
|
});
|
|
4124
3942
|
}
|
|
4125
3943
|
|
|
3944
|
+
const WEB_MCP_ENABLED = new InjectionToken('WEB_MCP_ENABLED', {
|
|
3945
|
+
providedIn: 'root',
|
|
3946
|
+
factory: () => false,
|
|
3947
|
+
});
|
|
3948
|
+
|
|
3949
|
+
const WEB_MCP_GATE = new InjectionToken('WEB_MCP_GATE');
|
|
3950
|
+
function createWebMcpGate() {
|
|
3951
|
+
const enabled = inject(WEB_MCP_ENABLED);
|
|
3952
|
+
const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
3953
|
+
const stateManager = inject(FormStateManager);
|
|
3954
|
+
const injector = inject(Injector);
|
|
3955
|
+
const destroyRef = inject(DestroyRef);
|
|
3956
|
+
const logger = inject(DynamicFormLogger);
|
|
3957
|
+
if (!enabled || !isBrowser)
|
|
3958
|
+
return computed(() => 'disabled');
|
|
3959
|
+
const registration = computed(() => {
|
|
3960
|
+
const options = stateManager.effectiveFormOptions().webMcp;
|
|
3961
|
+
return options ? { options: options, setup: stateManager.formSetup() } : undefined;
|
|
3962
|
+
}, /* @ts-ignore */
|
|
3963
|
+
...(ngDevMode ? [{ debugName: "registration" }] : /* istanbul ignore next */ []));
|
|
3964
|
+
const status = signal('idle', /* @ts-ignore */
|
|
3965
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
3966
|
+
let epoch;
|
|
3967
|
+
let epochId = 0;
|
|
3968
|
+
const closeEpoch = () => {
|
|
3969
|
+
epoch?.abort();
|
|
3970
|
+
epoch = undefined;
|
|
3971
|
+
};
|
|
3972
|
+
destroyRef.onDestroy(closeEpoch);
|
|
3973
|
+
explicitEffect([registration], ([input]) => {
|
|
3974
|
+
closeEpoch();
|
|
3975
|
+
epochId += 1;
|
|
3976
|
+
const thisEpoch = epochId;
|
|
3977
|
+
if (!input) {
|
|
3978
|
+
status.set('idle');
|
|
3979
|
+
return;
|
|
3980
|
+
}
|
|
3981
|
+
const controller = new AbortController();
|
|
3982
|
+
epoch = controller;
|
|
3983
|
+
status.set('registering');
|
|
3984
|
+
void import('./ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs')
|
|
3985
|
+
.then(({ bootstrapWebMcp }) => {
|
|
3986
|
+
if (thisEpoch !== epochId || controller.signal.aborted)
|
|
3987
|
+
return;
|
|
3988
|
+
return runInInjectionContext(injector, () => bootstrapWebMcp(input.options, controller.signal));
|
|
3989
|
+
})
|
|
3990
|
+
.then((result) => {
|
|
3991
|
+
if (thisEpoch !== epochId || controller.signal.aborted)
|
|
3992
|
+
return;
|
|
3993
|
+
if (result)
|
|
3994
|
+
status.set(result);
|
|
3995
|
+
})
|
|
3996
|
+
.catch((error) => {
|
|
3997
|
+
if (thisEpoch !== epochId)
|
|
3998
|
+
return;
|
|
3999
|
+
status.set('failed');
|
|
4000
|
+
logger.error('Failed to load the WebMCP registrar; this form will not be exposed to agents.', error);
|
|
4001
|
+
});
|
|
4002
|
+
});
|
|
4003
|
+
return status.asReadonly();
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4126
4006
|
const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
|
|
4127
4007
|
|
|
4128
4008
|
const AUTO_ID_PREFIX = 'df-';
|
|
@@ -4155,10 +4035,10 @@ class DynamicFormInstanceRegistry {
|
|
|
4155
4035
|
this.slots.delete(Number(id.slice(AUTO_ID_PREFIX.length)));
|
|
4156
4036
|
this._membership.update((m) => m + 1);
|
|
4157
4037
|
}
|
|
4158
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4159
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
4038
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DynamicFormInstanceRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4039
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DynamicFormInstanceRegistry, providedIn: 'root' });
|
|
4160
4040
|
}
|
|
4161
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4041
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DynamicFormInstanceRegistry, decorators: [{
|
|
4162
4042
|
type: Injectable,
|
|
4163
4043
|
args: [{ providedIn: 'root' }]
|
|
4164
4044
|
}] });
|
|
@@ -4209,10 +4089,10 @@ class FormIdPrefixService {
|
|
|
4209
4089
|
});
|
|
4210
4090
|
}
|
|
4211
4091
|
}
|
|
4212
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4213
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
4092
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FormIdPrefixService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4093
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FormIdPrefixService });
|
|
4214
4094
|
}
|
|
4215
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4095
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: FormIdPrefixService, decorators: [{
|
|
4216
4096
|
type: Injectable
|
|
4217
4097
|
}], ctorParameters: () => [] });
|
|
4218
4098
|
|
|
@@ -4265,6 +4145,7 @@ function coreProviders() {
|
|
|
4265
4145
|
deps: [FormStateManager],
|
|
4266
4146
|
},
|
|
4267
4147
|
FormIdPrefixService,
|
|
4148
|
+
FieldViewportObserver,
|
|
4268
4149
|
{
|
|
4269
4150
|
provide: FORM_ID_PREFIX,
|
|
4270
4151
|
useFactory: (svc) => svc.prefix,
|
|
@@ -4304,6 +4185,7 @@ function provideDynamicFormDI() {
|
|
|
4304
4185
|
...asyncExpressionProviders(),
|
|
4305
4186
|
...propertyDerivationProviders(),
|
|
4306
4187
|
...derivationProviders(),
|
|
4188
|
+
{ provide: WEB_MCP_GATE, useFactory: createWebMcpGate },
|
|
4307
4189
|
];
|
|
4308
4190
|
}
|
|
4309
4191
|
|
|
@@ -4318,10 +4200,10 @@ class EventDispatcher {
|
|
|
4318
4200
|
disconnect() {
|
|
4319
4201
|
this.bus = undefined;
|
|
4320
4202
|
}
|
|
4321
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4322
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
4203
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4204
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EventDispatcher });
|
|
4323
4205
|
}
|
|
4324
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4206
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: EventDispatcher, decorators: [{
|
|
4325
4207
|
type: Injectable
|
|
4326
4208
|
}] });
|
|
4327
4209
|
|
|
@@ -4329,10 +4211,10 @@ class DfTemplate {
|
|
|
4329
4211
|
dfTemplate = input.required(/* @ts-ignore */
|
|
4330
4212
|
...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
|
|
4331
4213
|
templateRef = inject(TemplateRef);
|
|
4332
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4333
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
4214
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4215
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.5", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
|
|
4334
4216
|
}
|
|
4335
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4217
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfTemplate, decorators: [{
|
|
4336
4218
|
type: Directive,
|
|
4337
4219
|
args: [{
|
|
4338
4220
|
selector: 'ng-template[dfTemplate]',
|
|
@@ -4346,10 +4228,10 @@ class DfFieldTemplateRegistry {
|
|
|
4346
4228
|
set(value) {
|
|
4347
4229
|
this._map.set(value);
|
|
4348
4230
|
}
|
|
4349
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4350
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
4231
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4232
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfFieldTemplateRegistry });
|
|
4351
4233
|
}
|
|
4352
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4234
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DfFieldTemplateRegistry, decorators: [{
|
|
4353
4235
|
type: Injectable
|
|
4354
4236
|
}] });
|
|
4355
4237
|
|
|
@@ -4368,6 +4250,7 @@ class DynamicForm {
|
|
|
4368
4250
|
logger = inject(DynamicFormLogger);
|
|
4369
4251
|
dispatcher = inject(EventDispatcher, { optional: true });
|
|
4370
4252
|
templateRegistry = inject(DfFieldTemplateRegistry);
|
|
4253
|
+
globalFieldWindowing = inject(FIELD_WINDOWING);
|
|
4371
4254
|
_projectedTemplates = contentChildren(DfTemplate, /* @ts-ignore */
|
|
4372
4255
|
...(ngDevMode ? [{ debugName: "_projectedTemplates" }] : /* istanbul ignore next */ []));
|
|
4373
4256
|
stateManager = this.connectDeps();
|
|
@@ -4392,20 +4275,31 @@ class DynamicForm {
|
|
|
4392
4275
|
submitting = this.stateManager.submitting;
|
|
4393
4276
|
fieldLoadingErrors = this.stateManager.fieldLoadingErrors;
|
|
4394
4277
|
derivationReady = inject(DERIVATION_RENDER_GATE);
|
|
4395
|
-
shouldRender = computed(() => this.stateManager.shouldRender() && this.derivationReady(), /* @ts-ignore */
|
|
4278
|
+
shouldRender = computed(() => this.stateManager.shouldRender() && this.stateManager.formSchemaReady() && this.derivationReady(), /* @ts-ignore */
|
|
4396
4279
|
...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
4280
|
+
webMcpStatus = inject(WEB_MCP_GATE);
|
|
4397
4281
|
resolvedFields = this.stateManager.resolvedFields;
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4282
|
+
fieldWindowing = computed(() => resolveFieldWindowing(this.globalFieldWindowing, this.effectiveFormOptions()?.fieldWindowing), /* @ts-ignore */
|
|
4283
|
+
...(ngDevMode ? [{ debugName: "fieldWindowing" }] : /* istanbul ignore next */ []));
|
|
4284
|
+
initializedComponents = signal(new Set(), /* @ts-ignore */
|
|
4285
|
+
...(ngDevMode ? [{ debugName: "initializedComponents" }] : /* istanbul ignore next */ []));
|
|
4286
|
+
initialPageReady = signal(false, /* @ts-ignore */
|
|
4287
|
+
...(ngDevMode ? [{ debugName: "initialPageReady" }] : /* istanbul ignore next */ []));
|
|
4288
|
+
initializationReady = computed(() => {
|
|
4289
|
+
const config = this.activeConfig();
|
|
4290
|
+
if (!config)
|
|
4291
|
+
return false;
|
|
4292
|
+
if (this.formModeDetection().mode === 'paged')
|
|
4293
|
+
return this.initialPageReady();
|
|
4294
|
+
const initialized = this.initializedComponents();
|
|
4295
|
+
const expected = [
|
|
4296
|
+
initializationComponentKey('dynamic-form', this.componentId),
|
|
4297
|
+
...collectInitializingContainerKeys(config.fields ?? []),
|
|
4298
|
+
];
|
|
4299
|
+
return expected.every((key) => initialized.has(key));
|
|
4401
4300
|
}, /* @ts-ignore */
|
|
4402
|
-
...(ngDevMode ? [{ debugName: "
|
|
4403
|
-
|
|
4404
|
-
eventBus: this.eventBus,
|
|
4405
|
-
totalComponentsCount: this.totalComponentsCount,
|
|
4406
|
-
injector: this.injector,
|
|
4407
|
-
componentId: this.componentId,
|
|
4408
|
-
});
|
|
4301
|
+
...(ngDevMode ? [{ debugName: "initializationReady" }] : /* istanbul ignore next */ []));
|
|
4302
|
+
initialized$ = toObservable(this.initializationReady, { injector: this.injector }).pipe(filter((ready) => ready), take(1), shareReplay({ bufferSize: 1, refCount: false }));
|
|
4409
4303
|
validityChange = outputFromObservable(toObservable(this.valid));
|
|
4410
4304
|
dirtyChange = outputFromObservable(toObservable(this.dirty));
|
|
4411
4305
|
submitted = outputFromObservable(this.stateManager.submitted$);
|
|
@@ -4413,18 +4307,46 @@ class DynamicForm {
|
|
|
4413
4307
|
cleared = outputFromObservable(this.eventBus.on('form-clear'));
|
|
4414
4308
|
events = outputFromObservable(this.eventBus.events$);
|
|
4415
4309
|
initialized = outputFromObservable(this.initialized$);
|
|
4310
|
+
activePageInitialized = outputFromObservable(this.eventBus.on('active-page-initialized'));
|
|
4416
4311
|
onPageChange = outputFromObservable(this.eventBus.on('page-change'));
|
|
4417
|
-
onPageNavigationStateChange = outputFromObservable(this.eventBus.on('
|
|
4312
|
+
onPageNavigationStateChange = outputFromObservable(this.eventBus.on('pager-state'));
|
|
4418
4313
|
constructor() {
|
|
4314
|
+
this.eventBus
|
|
4315
|
+
.on('component-initialized')
|
|
4316
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4317
|
+
.subscribe((event) => {
|
|
4318
|
+
const key = initializationComponentKey(event.componentType, event.componentId);
|
|
4319
|
+
this.initializedComponents.update((current) => {
|
|
4320
|
+
if (current.has(key))
|
|
4321
|
+
return current;
|
|
4322
|
+
const next = new Set(current);
|
|
4323
|
+
next.add(key);
|
|
4324
|
+
return next;
|
|
4325
|
+
});
|
|
4326
|
+
});
|
|
4327
|
+
this.eventBus
|
|
4328
|
+
.on('active-page-initialized')
|
|
4329
|
+
.pipe(takeUntilDestroyed(this.destroyRef), take(1))
|
|
4330
|
+
.subscribe(() => this.initialPageReady.set(true));
|
|
4419
4331
|
this.dispatcher?.connect(this.eventBus);
|
|
4420
4332
|
this.destroyRef.onDestroy(() => this.dispatcher?.disconnect());
|
|
4421
4333
|
inject(FORM_INITIALIZER, { optional: true });
|
|
4334
|
+
const preload = injectFormComponentPreloader();
|
|
4335
|
+
explicitEffect([this.config], ([config]) => preload.preloadConfig(config));
|
|
4422
4336
|
this.setupEffects();
|
|
4423
4337
|
this.setupEventHandlers();
|
|
4424
4338
|
}
|
|
4425
4339
|
onNativeSubmit(event) {
|
|
4426
4340
|
event.preventDefault();
|
|
4427
4341
|
this.eventBus.dispatch(FormSubmitEvent);
|
|
4342
|
+
}
|
|
4343
|
+
windowsField(field, index) {
|
|
4344
|
+
const w = this.fieldWindowing();
|
|
4345
|
+
return w.enabled && index >= w.eager && !isContainerField(field.fieldDef);
|
|
4346
|
+
}
|
|
4347
|
+
placeholderGridClass(field) {
|
|
4348
|
+
const grid = getGridClassString(field.fieldDef);
|
|
4349
|
+
return grid ? `df-field-placeholder ${grid}` : 'df-field-placeholder';
|
|
4428
4350
|
}
|
|
4429
4351
|
setupEffects() {
|
|
4430
4352
|
explicitEffect([this.formModeDetection, this.pageFieldDefinitions], ([{ mode }, pages]) => {
|
|
@@ -4432,8 +4354,8 @@ class DynamicForm {
|
|
|
4432
4354
|
this.eventBus.dispatch(ComponentInitializedEvent, 'dynamic-form', this.componentId);
|
|
4433
4355
|
}
|
|
4434
4356
|
});
|
|
4435
|
-
explicitEffect([this.resolvedFields, this.formModeDetection], ([
|
|
4436
|
-
if (mode === 'non-paged'
|
|
4357
|
+
explicitEffect([this.resolvedFields, this.formModeDetection, this.activeConfig], ([, { mode }, config]) => {
|
|
4358
|
+
if (config && mode === 'non-paged') {
|
|
4437
4359
|
afterNextRender(() => {
|
|
4438
4360
|
this.eventBus.dispatch(ComponentInitializedEvent, 'dynamic-form', this.componentId);
|
|
4439
4361
|
}, { injector: this.injector });
|
|
@@ -4468,8 +4390,8 @@ class DynamicForm {
|
|
|
4468
4390
|
error: (err) => this.logger.error('Submission handler error', err),
|
|
4469
4391
|
});
|
|
4470
4392
|
}
|
|
4471
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4472
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
4393
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4394
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: DynamicForm, isStandalone: true, selector: "form[dynamic-form]", inputs: { config: { classPropertyName: "config", publicName: "dynamic-form", isSignal: true, isRequired: true, transformFunction: null }, formOptions: { classPropertyName: "formOptions", publicName: "formOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted", reset: "reset", cleared: "cleared", events: "events", initialized: "initialized", activePageInitialized: "activePageInitialized", onPageChange: "onPageChange", onPageNavigationStateChange: "onPageNavigationStateChange" }, host: { attributes: { "novalidate": "" }, listeners: { "submit": "onNativeSubmit($event)" }, properties: { "class.disabled": "disabled()", "class.df-form-paged": "formModeDetection().mode === \"paged\"", "class.df-form-non-paged": "formModeDetection().mode === \"non-paged\"", "attr.data-form-mode": "formModeDetection().mode", "attr.id": "idPrefix() || null" }, classAttribute: "df-dynamic-form df-form" }, providers: [
|
|
4473
4395
|
provideDynamicFormDI(),
|
|
4474
4396
|
DfFieldTemplateRegistry,
|
|
4475
4397
|
{ provide: DF_FIELD_TEMPLATES, useFactory: () => inject(DfFieldTemplateRegistry).map },
|
|
@@ -4480,12 +4402,23 @@ class DynamicForm {
|
|
|
4480
4402
|
<div page-orchestrator [pageFields]="pageFieldDefinitions()" [form]="form()" [fieldSignalContext]="fieldSignalContext()"></div>
|
|
4481
4403
|
}
|
|
4482
4404
|
@case ('non-paged') {
|
|
4483
|
-
@for (field of resolvedFields(); track field.key) {
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4405
|
+
@for (field of resolvedFields(); track field.key; let i = $index) {
|
|
4406
|
+
@if (windowsField(field, i)) {
|
|
4407
|
+
@if (!field.hidden()) {
|
|
4408
|
+
@defer (on viewport) {
|
|
4409
|
+
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
4410
|
+
} @placeholder {
|
|
4411
|
+
<div
|
|
4412
|
+
[class]="placeholderGridClass(field)"
|
|
4413
|
+
[style.min-height]="fieldWindowing().placeholderHeight"
|
|
4414
|
+
[attr.data-field-key]="field.key"
|
|
4415
|
+
aria-hidden="true"
|
|
4416
|
+
></div>
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
} @else {
|
|
4420
|
+
<!-- DfFieldOutlet owns the hidden gate so an already-mounted field can be detached
|
|
4421
|
+
and restored without recreating its component and wrapper chain. -->
|
|
4489
4422
|
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
4490
4423
|
}
|
|
4491
4424
|
}
|
|
@@ -4495,9 +4428,9 @@ class DynamicForm {
|
|
|
4495
4428
|
}
|
|
4496
4429
|
}
|
|
4497
4430
|
}
|
|
4498
|
-
`, 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-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--df-form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{gap:0}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }, { kind: "component", type: PageOrchestratorComponent, selector: "div[page-orchestrator]", inputs: ["pageFields", "form", "fieldSignalContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4431
|
+
`, 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-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--df-form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{gap:0}.df-field-placeholder{display:block}\n"], dependencies: [{ kind: "directive", type: DfFieldOutlet, selector: "[dfFieldOutlet]", inputs: ["dfFieldOutlet", "dfFieldOutletEnvironmentInjector"] }, { kind: "component", type: PageOrchestratorComponent, selector: "div[page-orchestrator]", inputs: ["pageFields", "form", "fieldSignalContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [DfFieldOutlet]] });
|
|
4499
4432
|
}
|
|
4500
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4433
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: DynamicForm, decorators: [{
|
|
4501
4434
|
type: Component,
|
|
4502
4435
|
args: [{ selector: 'form[dynamic-form]', imports: [DfFieldOutlet, PageOrchestratorComponent], template: `
|
|
4503
4436
|
@if (shouldRender()) {
|
|
@@ -4506,12 +4439,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
4506
4439
|
<div page-orchestrator [pageFields]="pageFieldDefinitions()" [form]="form()" [fieldSignalContext]="fieldSignalContext()"></div>
|
|
4507
4440
|
}
|
|
4508
4441
|
@case ('non-paged') {
|
|
4509
|
-
@for (field of resolvedFields(); track field.key) {
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4442
|
+
@for (field of resolvedFields(); track field.key; let i = $index) {
|
|
4443
|
+
@if (windowsField(field, i)) {
|
|
4444
|
+
@if (!field.hidden()) {
|
|
4445
|
+
@defer (on viewport) {
|
|
4446
|
+
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
4447
|
+
} @placeholder {
|
|
4448
|
+
<div
|
|
4449
|
+
[class]="placeholderGridClass(field)"
|
|
4450
|
+
[style.min-height]="fieldWindowing().placeholderHeight"
|
|
4451
|
+
[attr.data-field-key]="field.key"
|
|
4452
|
+
aria-hidden="true"
|
|
4453
|
+
></div>
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
} @else {
|
|
4457
|
+
<!-- DfFieldOutlet owns the hidden gate so an already-mounted field can be detached
|
|
4458
|
+
and restored without recreating its component and wrapper chain. -->
|
|
4515
4459
|
<ng-container *dfFieldOutlet="field; environmentInjector: environmentInjector" />
|
|
4516
4460
|
}
|
|
4517
4461
|
}
|
|
@@ -4533,30 +4477,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
4533
4477
|
'[attr.data-form-mode]': 'formModeDetection().mode',
|
|
4534
4478
|
'[attr.id]': 'idPrefix() || null',
|
|
4535
4479
|
'(submit)': 'onNativeSubmit($event)',
|
|
4536
|
-
}, 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-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--df-form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{gap:0}\n"] }]
|
|
4537
|
-
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "dynamic-form", required: true }] }], formOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "formOptions", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], _projectedTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DfTemplate), { isSignal: true }] }], validityChange: [{ type: i0.Output, args: ["validityChange"] }], dirtyChange: [{ type: i0.Output, args: ["dirtyChange"] }], submitted: [{ type: i0.Output, args: ["submitted"] }], reset: [{ type: i0.Output, args: ["reset"] }], cleared: [{ type: i0.Output, args: ["cleared"] }], events: [{ type: i0.Output, args: ["events"] }], initialized: [{ type: i0.Output, args: ["initialized"] }], onPageChange: [{ type: i0.Output, args: ["onPageChange"] }], onPageNavigationStateChange: [{ type: i0.Output, args: ["onPageNavigationStateChange"] }] } });
|
|
4538
|
-
function countContainersRecursive(fields) {
|
|
4539
|
-
let count = 0;
|
|
4540
|
-
for (const field of fields) {
|
|
4541
|
-
if (isContainerField(field)) {
|
|
4542
|
-
count += 1;
|
|
4543
|
-
if (hasChildFields(field)) {
|
|
4544
|
-
const children = field.fields;
|
|
4545
|
-
if (Array.isArray(children)) {
|
|
4546
|
-
for (const child of children) {
|
|
4547
|
-
if (Array.isArray(child)) {
|
|
4548
|
-
count += countContainersRecursive(child);
|
|
4549
|
-
}
|
|
4550
|
-
else if (child != null && isContainerField(child)) {
|
|
4551
|
-
count += countContainersRecursive([child]);
|
|
4552
|
-
}
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
}
|
|
4556
|
-
}
|
|
4557
|
-
}
|
|
4558
|
-
return count;
|
|
4559
|
-
}
|
|
4480
|
+
}, 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-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--df-form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{gap:0}.df-field-placeholder{display:block}\n"] }]
|
|
4481
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "dynamic-form", required: true }] }], formOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "formOptions", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], _projectedTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DfTemplate), { isSignal: true }] }], validityChange: [{ type: i0.Output, args: ["validityChange"] }], dirtyChange: [{ type: i0.Output, args: ["dirtyChange"] }], submitted: [{ type: i0.Output, args: ["submitted"] }], reset: [{ type: i0.Output, args: ["reset"] }], cleared: [{ type: i0.Output, args: ["cleared"] }], events: [{ type: i0.Output, args: ["events"] }], initialized: [{ type: i0.Output, args: ["initialized"] }], activePageInitialized: [{ type: i0.Output, args: ["activePageInitialized"] }], onPageChange: [{ type: i0.Output, args: ["onPageChange"] }], onPageNavigationStateChange: [{ type: i0.Output, args: ["onPageNavigationStateChange"] }] } });
|
|
4560
4482
|
|
|
4561
4483
|
const BUILT_IN_ADDON_TYPES = [
|
|
4562
4484
|
{
|
|
@@ -4604,35 +4526,35 @@ const DISPLAY_FIELD_TYPES_BASE = {
|
|
|
4604
4526
|
const BUILT_IN_FIELDS = [
|
|
4605
4527
|
{
|
|
4606
4528
|
name: 'row',
|
|
4607
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
4529
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs'),
|
|
4608
4530
|
mapper: rowFieldMapper,
|
|
4609
4531
|
valueHandling: 'flatten',
|
|
4610
4532
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
4611
4533
|
},
|
|
4612
4534
|
{
|
|
4613
4535
|
name: 'group',
|
|
4614
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-
|
|
4536
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs'),
|
|
4615
4537
|
mapper: groupFieldMapper,
|
|
4616
4538
|
valueHandling: 'include',
|
|
4617
4539
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
4618
4540
|
},
|
|
4619
4541
|
{
|
|
4620
4542
|
name: 'array',
|
|
4621
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-
|
|
4543
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs'),
|
|
4622
4544
|
mapper: arrayFieldMapper,
|
|
4623
4545
|
valueHandling: 'include',
|
|
4624
4546
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
4625
4547
|
},
|
|
4626
4548
|
{
|
|
4627
4549
|
name: 'page',
|
|
4628
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-
|
|
4550
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs'),
|
|
4629
4551
|
mapper: pageFieldMapper,
|
|
4630
4552
|
valueHandling: 'flatten',
|
|
4631
4553
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
4632
4554
|
},
|
|
4633
4555
|
{
|
|
4634
4556
|
name: 'text',
|
|
4635
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-
|
|
4557
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs'),
|
|
4636
4558
|
mapper: textFieldMapper,
|
|
4637
4559
|
valueHandling: 'exclude',
|
|
4638
4560
|
...DISPLAY_FIELD_TYPES_BASE,
|
|
@@ -4643,7 +4565,7 @@ const BUILT_IN_FIELDS = [
|
|
|
4643
4565
|
},
|
|
4644
4566
|
{
|
|
4645
4567
|
name: 'container',
|
|
4646
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
4568
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs'),
|
|
4647
4569
|
mapper: containerFieldMapper,
|
|
4648
4570
|
valueHandling: 'flatten',
|
|
4649
4571
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
@@ -4652,11 +4574,16 @@ const BUILT_IN_FIELDS = [
|
|
|
4652
4574
|
const BUILT_IN_WRAPPERS = [
|
|
4653
4575
|
{
|
|
4654
4576
|
wrapperName: 'css',
|
|
4655
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-
|
|
4577
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs'),
|
|
4656
4578
|
},
|
|
4657
4579
|
{
|
|
4658
4580
|
wrapperName: 'row',
|
|
4659
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-
|
|
4581
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs'),
|
|
4582
|
+
},
|
|
4583
|
+
{
|
|
4584
|
+
wrapperName: 'field-errors',
|
|
4585
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs'),
|
|
4586
|
+
rendersFieldErrors: true,
|
|
4660
4587
|
},
|
|
4661
4588
|
];
|
|
4662
4589
|
|
|
@@ -4717,7 +4644,8 @@ function provideDynamicForm(...items) {
|
|
|
4717
4644
|
const wrapperBundles = items.filter(isWrappersBundle);
|
|
4718
4645
|
const features = items.filter(isDynamicFormFeature);
|
|
4719
4646
|
const fields = [...BUILT_IN_FIELDS, ...fieldTypes];
|
|
4720
|
-
const
|
|
4647
|
+
const customWrappers = [...wrapperTypes, ...wrapperBundles.flatMap((bundle) => bundle.ɵdefinitions)];
|
|
4648
|
+
const wrappers = [...BUILT_IN_WRAPPERS, ...customWrappers];
|
|
4721
4649
|
const featureProviders = [];
|
|
4722
4650
|
const hasLoggerFeature = features.some((feature) => feature.ɵkind === 'logger');
|
|
4723
4651
|
features.forEach((feature) => {
|
|
@@ -4732,7 +4660,7 @@ function provideDynamicForm(...items) {
|
|
|
4732
4660
|
const logger = inject(DynamicFormLogger);
|
|
4733
4661
|
const registry = new Map();
|
|
4734
4662
|
fields.forEach((fieldType) => {
|
|
4735
|
-
|
|
4663
|
+
if (DEV_MODE && registry.has(fieldType.name)) {
|
|
4736
4664
|
logger.warn(`Field type "${fieldType.name}" is already registered. Overwriting.`);
|
|
4737
4665
|
}
|
|
4738
4666
|
registry.set(fieldType.name, fieldType);
|
|
@@ -4758,11 +4686,18 @@ function provideDynamicForm(...items) {
|
|
|
4758
4686
|
useFactory: () => {
|
|
4759
4687
|
const logger = inject(DynamicFormLogger);
|
|
4760
4688
|
const registry = new Map();
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4689
|
+
const builtInNames = new Set(BUILT_IN_WRAPPERS.map((w) => w.wrapperName));
|
|
4690
|
+
BUILT_IN_WRAPPERS.forEach((wrapperType) => registry.set(wrapperType.wrapperName, wrapperType));
|
|
4691
|
+
const seenCustom = DEV_MODE ? new Set() : null;
|
|
4692
|
+
customWrappers.forEach((wrapperType) => {
|
|
4693
|
+
const name = wrapperType.wrapperName;
|
|
4694
|
+
if (seenCustom) {
|
|
4695
|
+
if (seenCustom.has(name) && !builtInNames.has(name)) {
|
|
4696
|
+
logger.warn(`Wrapper type "${name}" is already registered. Overwriting.`);
|
|
4697
|
+
}
|
|
4698
|
+
seenCustom.add(name);
|
|
4764
4699
|
}
|
|
4765
|
-
registry.set(
|
|
4700
|
+
registry.set(name, wrapperType);
|
|
4766
4701
|
});
|
|
4767
4702
|
return registry;
|
|
4768
4703
|
},
|
|
@@ -4793,7 +4728,7 @@ function provideDynamicForm(...items) {
|
|
|
4793
4728
|
registry.set(def.type, def);
|
|
4794
4729
|
}
|
|
4795
4730
|
for (const def of contributed) {
|
|
4796
|
-
|
|
4731
|
+
if (DEV_MODE && registry.has(def.type)) {
|
|
4797
4732
|
logger.warn(`Addon type "${def.type}" is already registered. Overwriting.`);
|
|
4798
4733
|
}
|
|
4799
4734
|
registry.set(def.type, def);
|
|
@@ -4841,6 +4776,32 @@ function withValueExclusionDefaults(config) {
|
|
|
4841
4776
|
return createFeature('value-exclusion', [{ provide: VALUE_EXCLUSION_DEFAULTS, useValue: resolved }]);
|
|
4842
4777
|
}
|
|
4843
4778
|
|
|
4779
|
+
function withPagePreload(window) {
|
|
4780
|
+
const resolved = clampWindowSize(window, 1);
|
|
4781
|
+
return createFeature('page-preload', [{ provide: PAGE_PRELOAD_WINDOW, useValue: resolved }]);
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4784
|
+
function withFieldWindowing(config) {
|
|
4785
|
+
const resolved = {
|
|
4786
|
+
enabled: true,
|
|
4787
|
+
eager: clampWindowSize(config?.eager, 12),
|
|
4788
|
+
placeholderHeight: config?.placeholderHeight ?? '4rem',
|
|
4789
|
+
park: resolveFieldParking({ enabled: true, margin: '100%' }, config?.park),
|
|
4790
|
+
};
|
|
4791
|
+
return createFeature('field-windowing', [{ provide: FIELD_WINDOWING, useValue: resolved }]);
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
function withExperimentalWebMcp() {
|
|
4795
|
+
return createFeature('web-mcp', [{ provide: WEB_MCP_ENABLED, useValue: true }]);
|
|
4796
|
+
}
|
|
4797
|
+
|
|
4798
|
+
const VALIDATION_EXECUTION_DEFAULTS = new InjectionToken('VALIDATION_EXECUTION_DEFAULTS', {
|
|
4799
|
+
providedIn: 'root',
|
|
4800
|
+
factory: () => ({
|
|
4801
|
+
validateWhenHidden: false,
|
|
4802
|
+
}),
|
|
4803
|
+
});
|
|
4804
|
+
|
|
4844
4805
|
function withValidationExecutionDefaults(config) {
|
|
4845
4806
|
const resolved = {
|
|
4846
4807
|
validateWhenHidden: config?.validateWhenHidden ?? false,
|
|
@@ -4861,10 +4822,10 @@ class TextAddonComponent {
|
|
|
4861
4822
|
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
4862
4823
|
fieldInputs = input(/* @ts-ignore */
|
|
4863
4824
|
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
4864
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4865
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.
|
|
4825
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TextAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4826
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: TextAddonComponent, isStandalone: true, selector: "df-text-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "\"true\"" } }, ngImport: i0, template: `<span>{{ addon().text | dynamicText | async }}</span>`, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4866
4827
|
}
|
|
4867
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4828
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TextAddonComponent, decorators: [{
|
|
4868
4829
|
type: Component,
|
|
4869
4830
|
args: [{
|
|
4870
4831
|
selector: 'df-text-addon',
|
|
@@ -4904,14 +4865,14 @@ class TemplateAddonComponent {
|
|
|
4904
4865
|
}
|
|
4905
4866
|
});
|
|
4906
4867
|
}
|
|
4907
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4908
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
4868
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TemplateAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4869
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: TemplateAddonComponent, isStandalone: true, selector: "df-template-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4909
4870
|
@if (template(); as tpl) {
|
|
4910
4871
|
<ng-container *ngTemplateOutlet="tpl; context: outletContext()" />
|
|
4911
4872
|
}
|
|
4912
4873
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4913
4874
|
}
|
|
4914
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4875
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TemplateAddonComponent, decorators: [{
|
|
4915
4876
|
type: Component,
|
|
4916
4877
|
args: [{
|
|
4917
4878
|
selector: 'df-template-addon',
|
|
@@ -4959,14 +4920,14 @@ class ComponentAddonComponent {
|
|
|
4959
4920
|
});
|
|
4960
4921
|
});
|
|
4961
4922
|
}
|
|
4962
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
4963
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
4923
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ComponentAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4924
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ComponentAddonComponent, isStandalone: true, selector: "df-component-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4964
4925
|
@if (resolvedComponent(); as cmp) {
|
|
4965
4926
|
<ng-container *ngComponentOutlet="cmp; inputs: inputs()" />
|
|
4966
4927
|
}
|
|
4967
4928
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4968
4929
|
}
|
|
4969
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
4930
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ComponentAddonComponent, decorators: [{
|
|
4970
4931
|
type: Component,
|
|
4971
4932
|
args: [{
|
|
4972
4933
|
selector: 'df-component-addon',
|
|
@@ -5031,6 +4992,128 @@ function sanitizeFormConfigPure(config, fieldRegistry, typeRegistry, options = {
|
|
|
5031
4992
|
return { sanitized: { ...config, fields: fields }, warnings };
|
|
5032
4993
|
}
|
|
5033
4994
|
|
|
4995
|
+
class ActivePageInitializedEvent {
|
|
4996
|
+
pageIndex;
|
|
4997
|
+
pageKey;
|
|
4998
|
+
type = 'active-page-initialized';
|
|
4999
|
+
constructor(pageIndex, pageKey) {
|
|
5000
|
+
this.pageIndex = pageIndex;
|
|
5001
|
+
this.pageKey = pageKey;
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
|
|
5005
|
+
class GoToPageEvent {
|
|
5006
|
+
pageIndex;
|
|
5007
|
+
options;
|
|
5008
|
+
type = 'go-to-page';
|
|
5009
|
+
constructor(
|
|
5010
|
+
pageIndex,
|
|
5011
|
+
options) {
|
|
5012
|
+
this.pageIndex = pageIndex;
|
|
5013
|
+
this.options = options;
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
|
|
5017
|
+
class NextPageEvent {
|
|
5018
|
+
type = 'next-page';
|
|
5019
|
+
}
|
|
5020
|
+
|
|
5021
|
+
class PreviousPageEvent {
|
|
5022
|
+
type = 'previous-page';
|
|
5023
|
+
}
|
|
5024
|
+
|
|
5025
|
+
class FormResetEvent {
|
|
5026
|
+
type = 'form-reset';
|
|
5027
|
+
}
|
|
5028
|
+
|
|
5029
|
+
class FormClearEvent {
|
|
5030
|
+
type = 'form-clear';
|
|
5031
|
+
}
|
|
5032
|
+
|
|
5033
|
+
class AppendArrayItemEvent {
|
|
5034
|
+
arrayKey;
|
|
5035
|
+
template;
|
|
5036
|
+
type = 'append-array-item';
|
|
5037
|
+
constructor(
|
|
5038
|
+
arrayKey,
|
|
5039
|
+
template) {
|
|
5040
|
+
this.arrayKey = arrayKey;
|
|
5041
|
+
this.template = template;
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
|
|
5045
|
+
class PrependArrayItemEvent {
|
|
5046
|
+
arrayKey;
|
|
5047
|
+
template;
|
|
5048
|
+
type = 'prepend-array-item';
|
|
5049
|
+
constructor(
|
|
5050
|
+
arrayKey,
|
|
5051
|
+
template) {
|
|
5052
|
+
this.arrayKey = arrayKey;
|
|
5053
|
+
this.template = template;
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
class InsertArrayItemEvent {
|
|
5058
|
+
arrayKey;
|
|
5059
|
+
index;
|
|
5060
|
+
template;
|
|
5061
|
+
type = 'insert-array-item';
|
|
5062
|
+
constructor(
|
|
5063
|
+
arrayKey,
|
|
5064
|
+
index,
|
|
5065
|
+
template) {
|
|
5066
|
+
this.arrayKey = arrayKey;
|
|
5067
|
+
this.index = index;
|
|
5068
|
+
this.template = template;
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
|
|
5072
|
+
class PopArrayItemEvent {
|
|
5073
|
+
arrayKey;
|
|
5074
|
+
type = 'pop-array-item';
|
|
5075
|
+
constructor(
|
|
5076
|
+
arrayKey) {
|
|
5077
|
+
this.arrayKey = arrayKey;
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
|
|
5081
|
+
class ShiftArrayItemEvent {
|
|
5082
|
+
arrayKey;
|
|
5083
|
+
type = 'shift-array-item';
|
|
5084
|
+
constructor(
|
|
5085
|
+
arrayKey) {
|
|
5086
|
+
this.arrayKey = arrayKey;
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
class MoveArrayItemEvent {
|
|
5091
|
+
arrayKey;
|
|
5092
|
+
fromIndex;
|
|
5093
|
+
toIndex;
|
|
5094
|
+
type = 'move-array-item';
|
|
5095
|
+
constructor(
|
|
5096
|
+
arrayKey,
|
|
5097
|
+
fromIndex,
|
|
5098
|
+
toIndex) {
|
|
5099
|
+
this.arrayKey = arrayKey;
|
|
5100
|
+
this.fromIndex = fromIndex;
|
|
5101
|
+
this.toIndex = toIndex;
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
|
|
5105
|
+
class RemoveAtIndexEvent {
|
|
5106
|
+
arrayKey;
|
|
5107
|
+
index;
|
|
5108
|
+
type = 'remove-at-index';
|
|
5109
|
+
constructor(
|
|
5110
|
+
arrayKey,
|
|
5111
|
+
index) {
|
|
5112
|
+
this.arrayKey = arrayKey;
|
|
5113
|
+
this.index = index;
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
|
|
5034
5117
|
function arrayEvent(arrayKey) {
|
|
5035
5118
|
return {
|
|
5036
5119
|
append: (template) => new AppendArrayItemEvent(arrayKey, template),
|
|
@@ -5046,5 +5129,5 @@ function arrayEvent(arrayKey) {
|
|
|
5046
5129
|
|
|
5047
5130
|
|
|
5048
5131
|
|
|
5049
|
-
export {
|
|
5050
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
5132
|
+
export { arrayEvent as $, ArrayItemRegistryService as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfFieldOutlet as D, ConsoleLogger as E, FIELD_WINDOWING as F, DfTemplate as G, DynamicForm as H, EventDispatcher as I, FormClearEvent as J, FormResetEvent as K, GoToPageEvent as L, InsertArrayItemEvent as M, MoveArrayItemEvent as N, NextPageEvent as O, PROPERTY_OVERRIDE_STORE as P, PageChangeEvent as Q, PagerStateEvent as R, SchemaRegistryService as S, PopArrayItemEvent as T, PrependArrayItemEvent as U, VALIDATION_EXECUTION_DEFAULTS as V, PreviousPageEvent as W, RemoveAtIndexEvent as X, ShiftArrayItemEvent as Y, TemplateAddonComponent as Z, TextAddonComponent as _, createFieldResolutionPipe as a, createWrappers as a0, isWrappersBundle as a1, provideDynamicForm as a2, sanitizeFormConfig as a3, withAddonActions as a4, withCustomAddon as a5, withEventFormValue as a6, withExperimentalWebMcp as a7, withFieldWindowing as a8, withLegacyStatusClasses as a9, withLoggerConfig as aa, withPagePreload as ab, withValidationExecutionDefaults as ac, withValueExclusionDefaults as ad, wrapperProps as ae, isSameWrapperChain as b, computeContainerHostClasses as c, initializationComponentPath as d, createWrapperChainController as e, createHiddenSignal as f, createRenderReadySignal as g, getNormalizedArrayMetadata as h, injectFieldRegistry as i, emitComponentInitialized as j, getFieldDefaultValue as k, resolveFieldWindowing as l, mapFieldToInputs as m, collectInitializingContainerKeys as n, ActivePageInitializedEvent as o, initializationComponentKey as p, DEV_MODE as q, resolveWrappers as r, setupContainerInitEffect as s, traverseFieldsWithContext as t, DERIVATION_WARNING_TRACKER as u, FormStateManager as v, DERIVATION_LOG_CONFIG as w, FormSubmitEvent as x, AppendArrayItemEvent as y, ComponentAddonComponent as z };
|
|
5133
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs.map
|