@ng-forge/dynamic-forms 0.10.0-next.4 → 1.0.0-rc.2
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 +16 -0
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs → ng-forge-dynamic-forms-array-field.component-8rodySeE.mjs} +47 -26
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-8rodySeE.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-C8kpF0tw.mjs +2347 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-C8kpF0tw.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs → ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs} +22 -14
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs → ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs} +3 -2
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs → ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs} +8 -6
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs → ng-forge-dynamic-forms-group-field.component-Ca4AqdZu.mjs} +42 -24
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-Ca4AqdZu.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +122 -81
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +297 -56
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BJi_rtey.mjs} +535 -3055
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BJi_rtey.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs → ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs} +26 -16
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs → ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs} +6 -5
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs → ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs} +18 -11
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map → ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +2 -2
- package/package.json +4 -4
- package/types/ng-forge-dynamic-forms-integration.d.ts +5 -5
- package/types/ng-forge-dynamic-forms-internal.d.ts +1943 -1765
- package/types/ng-forge-dynamic-forms.d.ts +59 -267
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map +0 -1
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { reflectComponentType, Injector, inject, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, linkedSignal, ChangeDetectionStrategy, Component,
|
|
2
|
+
import { reflectComponentType, Injector, inject, EnvironmentInjector, DestroyRef, computed, signal, input, ViewContainerRef, Directive, linkedSignal, ChangeDetectionStrategy, Component, Injectable, InjectionToken, isSignal, runInInjectionContext, afterNextRender, untracked, PendingTasks, ElementRef, PLATFORM_ID, isDevMode, TemplateRef, model, contentChildren, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
4
|
-
import { DynamicFormLogger, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, FieldContextRegistryService, FunctionRegistryService, FORM_OPTIONS, evaluateCondition, FIELD_SIGNAL_CONTEXT, isRowField, isContainerField, isArrayField, hasChildFields, isSimplifiedArrayField, DynamicFormError, isStateLogicConfig, isDerivationLogicConfig, createDebouncedLogicFunction, createLogicFunction, ExpressionParser, applyValidator, isPageField,
|
|
4
|
+
import { DynamicFormLogger, WRAPPER_COMPONENT_CACHE, WRAPPER_REGISTRY, toReadonlyFieldTreeCached, writeToFieldValue, WRAPPER_AUTO_ASSOCIATIONS, DEFAULT_WRAPPERS, READONLY_FIELD_TREE_CACHE, EventBus, FieldContextRegistryService, FunctionRegistryService, FORM_OPTIONS, evaluateCondition, FIELD_SIGNAL_CONTEXT, isRowField, isContainerField, isArrayField, hasChildFields, isSimplifiedArrayField, DynamicFormError, isStateLogicConfig, isDerivationLogicConfig, createDebouncedLogicFunction, createLogicFunction, ExpressionParser, applyValidator, isPageField, isGenericContainerField, isGroupField, getFieldValueHandling, normalizeFieldsArray, getNestedValue, isCrossFieldValidator, extractStringDependencies, isCrossFieldBuiltInValidator, hasCrossFieldWhenCondition, extractExpressionDependencies, isCrossFieldStateLogic, isCrossFieldSchema, keyBy, mapValues, isEqual, hasTargetProperty, ARRAY_CONTEXT, GROUP_CONTEXT, FORM_ID_PREFIX, baseFieldMapper, detectFormMode, validatePageNesting, FIELD_REGISTRY, assertNever, ADDON_TYPE_REGISTRY, deepMergeDefaults, derivedFromDeferred, createWarningTracker, RootFormRegistryService, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, EXTERNAL_DATA, DEPRECATION_WARNING_TRACKER, createAddonActionRegistry, ADDON_ACTION_REGISTRY, HttpConditionCache, HTTP_CONDITION_CACHE, HttpConditionFunctionCacheService, AsyncConditionFunctionCacheService, DynamicValueFunctionCacheService, LogicFunctionCacheService, setupInitializationTracking, ComponentInitializedEvent, 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';
|
|
5
5
|
import { takeUntilDestroyed, toObservable, toSignal, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, debounceTime, distinctUntilChanged, tap, pipe, scan, Observable, Subject, concatMap, filter, take
|
|
6
|
+
import { of, forkJoin, from, catchError, map, switchMap, isObservable, firstValueFrom, exhaustMap, EMPTY, debounceTime, distinctUntilChanged, tap, pipe, scan, Observable, Subject, concatMap, filter, take } from 'rxjs';
|
|
7
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
8
|
import { isStandardSchemaMarker } from '@ng-forge/dynamic-forms/schema';
|
|
9
|
-
import { isPlatformBrowser,
|
|
10
|
-
import { HttpClient } from '@angular/common/http';
|
|
11
|
-
import { catchError as catchError$1 } from 'rxjs/operators';
|
|
9
|
+
import { isPlatformBrowser, AsyncPipe, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';
|
|
12
10
|
import { NG_STATUS_CLASSES } from '@angular/forms/signals/compat';
|
|
13
11
|
|
|
14
12
|
const EMPTY_WRAPPERS = Object.freeze([]);
|
|
@@ -258,7 +256,8 @@ function buildFieldInputs(rawInputs, cache, fieldType) {
|
|
|
258
256
|
|
|
259
257
|
const EMPTY_STATE = Object.freeze({ phase: 'empty' });
|
|
260
258
|
class FieldComponentSlot {
|
|
261
|
-
state = signal(EMPTY_STATE,
|
|
259
|
+
state = signal(EMPTY_STATE, /* @ts-ignore */
|
|
260
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
262
261
|
lastInputs = null;
|
|
263
262
|
phase = () => this.state().phase;
|
|
264
263
|
snapshot = () => this.state();
|
|
@@ -342,8 +341,10 @@ class FieldComponentSlot {
|
|
|
342
341
|
}
|
|
343
342
|
|
|
344
343
|
class DfFieldOutlet {
|
|
345
|
-
dfFieldOutlet = input.required(
|
|
346
|
-
|
|
344
|
+
dfFieldOutlet = input.required(/* @ts-ignore */
|
|
345
|
+
...(ngDevMode ? [{ debugName: "dfFieldOutlet" }] : /* istanbul ignore next */ []));
|
|
346
|
+
dfFieldOutletEnvironmentInjector = input(undefined, /* @ts-ignore */
|
|
347
|
+
...(ngDevMode ? [{ debugName: "dfFieldOutletEnvironmentInjector" }] : /* istanbul ignore next */ []));
|
|
347
348
|
vcrRef = inject(ViewContainerRef);
|
|
348
349
|
vcr = signal(this.vcrRef).asReadonly();
|
|
349
350
|
destroyRef = inject(DestroyRef);
|
|
@@ -351,14 +352,20 @@ class DfFieldOutlet {
|
|
|
351
352
|
defaultWrappersSignal = inject(DEFAULT_WRAPPERS, { optional: true });
|
|
352
353
|
readonlyFieldCache = inject(READONLY_FIELD_TREE_CACHE);
|
|
353
354
|
fieldComponent = new FieldComponentSlot();
|
|
354
|
-
componentIdentity = computed(() => this.dfFieldOutlet().component,
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
componentIdentity = computed(() => this.dfFieldOutlet().component, /* @ts-ignore */
|
|
356
|
+
...(ngDevMode ? [{ debugName: "componentIdentity" }] : /* istanbul ignore next */ []));
|
|
357
|
+
renderReady = computed(() => this.dfFieldOutlet().renderReady() && !this.dfFieldOutlet().hidden(), /* @ts-ignore */
|
|
358
|
+
...(ngDevMode ? [{ debugName: "renderReady" }] : /* istanbul ignore next */ []));
|
|
359
|
+
rawInputs = computed(() => this.dfFieldOutlet().inputs(), /* @ts-ignore */
|
|
360
|
+
...(ngDevMode ? [{ debugName: "rawInputs" }] : /* istanbul ignore next */ []));
|
|
357
361
|
wrappers = computed(() => resolveWrappers(this.dfFieldOutlet().fieldDef, this.defaultWrappersSignal?.(), this.wrapperAutoAssociations), { ...(ngDevMode ? { debugName: "wrappers" } : /* istanbul ignore next */ {}), equal: isSameWrapperChain });
|
|
358
|
-
fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type),
|
|
362
|
+
fieldInputs = computed(() => buildFieldInputs(this.rawInputs(), this.readonlyFieldCache, this.dfFieldOutlet().fieldDef.type), /* @ts-ignore */
|
|
363
|
+
...(ngDevMode ? [{ debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
359
364
|
defaultEnvInjector = inject(EnvironmentInjector);
|
|
360
|
-
fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector,
|
|
361
|
-
|
|
365
|
+
fieldEnvInjector = computed(() => this.dfFieldOutletEnvironmentInjector() ?? this.defaultEnvInjector, /* @ts-ignore */
|
|
366
|
+
...(ngDevMode ? [{ debugName: "fieldEnvInjector" }] : /* istanbul ignore next */ []));
|
|
367
|
+
fieldInjector = computed(() => this.dfFieldOutlet().injector, /* @ts-ignore */
|
|
368
|
+
...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
|
|
362
369
|
constructor() {
|
|
363
370
|
createWrapperChainController({
|
|
364
371
|
vcr: this.vcr,
|
|
@@ -376,10 +383,10 @@ class DfFieldOutlet {
|
|
|
376
383
|
explicitEffect([this.rawInputs, this.fieldInputs], ([rawInputs, fieldInputs]) => this.fieldComponent.pushInputs(rawInputs, fieldInputs));
|
|
377
384
|
this.destroyRef.onDestroy(() => this.fieldComponent.destroyOnTeardown());
|
|
378
385
|
}
|
|
379
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
380
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
386
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
387
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", 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 });
|
|
381
388
|
}
|
|
382
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
389
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldOutlet, decorators: [{
|
|
383
390
|
type: Directive,
|
|
384
391
|
args: [{
|
|
385
392
|
selector: '[dfFieldOutlet]',
|
|
@@ -415,7 +422,7 @@ function createSubmissionHandler(options) {
|
|
|
415
422
|
}));
|
|
416
423
|
}
|
|
417
424
|
|
|
418
|
-
class
|
|
425
|
+
class FormSubmitEvent {
|
|
419
426
|
type = 'submit';
|
|
420
427
|
}
|
|
421
428
|
|
|
@@ -547,26 +554,32 @@ class PageOrchestratorComponent {
|
|
|
547
554
|
fieldContextRegistry = inject(FieldContextRegistryService);
|
|
548
555
|
functionRegistry = inject(FunctionRegistryService);
|
|
549
556
|
formOptions = inject(FORM_OPTIONS, { optional: true });
|
|
550
|
-
pageFields = input.required(
|
|
551
|
-
|
|
552
|
-
|
|
557
|
+
pageFields = input.required(/* @ts-ignore */
|
|
558
|
+
...(ngDevMode ? [{ debugName: "pageFields" }] : /* istanbul ignore next */ []));
|
|
559
|
+
form = input.required(/* @ts-ignore */
|
|
560
|
+
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
561
|
+
fieldSignalContext = input.required(/* @ts-ignore */
|
|
562
|
+
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
553
563
|
pageHiddenStates = computed(() => {
|
|
554
564
|
const pages = this.pageFields();
|
|
555
565
|
return pages.map((page) => this.evaluatePageHidden(page));
|
|
556
|
-
},
|
|
566
|
+
}, /* @ts-ignore */
|
|
567
|
+
...(ngDevMode ? [{ debugName: "pageHiddenStates" }] : /* istanbul ignore next */ []));
|
|
557
568
|
visiblePageIndices = computed(() => {
|
|
558
569
|
const hiddenStates = this.pageHiddenStates();
|
|
559
570
|
return hiddenStates
|
|
560
571
|
.map((hidden, index) => ({ index, hidden }))
|
|
561
572
|
.filter((item) => !item.hidden)
|
|
562
573
|
.map((item) => item.index);
|
|
563
|
-
},
|
|
574
|
+
}, /* @ts-ignore */
|
|
575
|
+
...(ngDevMode ? [{ debugName: "visiblePageIndices" }] : /* istanbul ignore next */ []));
|
|
564
576
|
currentPageIndex = linkedSignal(() => {
|
|
565
577
|
const totalPages = this.pageFields().length;
|
|
566
578
|
if (totalPages === 0)
|
|
567
579
|
return 0;
|
|
568
580
|
return 0;
|
|
569
|
-
},
|
|
581
|
+
}, /* @ts-ignore */
|
|
582
|
+
...(ngDevMode ? [{ debugName: "currentPageIndex" }] : /* istanbul ignore next */ []));
|
|
570
583
|
state = computed(() => {
|
|
571
584
|
const currentIndex = this.currentPageIndex();
|
|
572
585
|
const totalPages = this.pageFields().length;
|
|
@@ -581,7 +594,8 @@ class PageOrchestratorComponent {
|
|
|
581
594
|
isLastPage: isLastVisiblePage,
|
|
582
595
|
navigationDisabled: false,
|
|
583
596
|
};
|
|
584
|
-
},
|
|
597
|
+
}, /* @ts-ignore */
|
|
598
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
585
599
|
currentPageValid = computed(() => {
|
|
586
600
|
const currentIndex = this.currentPageIndex();
|
|
587
601
|
const pages = this.pageFields();
|
|
@@ -602,11 +616,13 @@ class PageOrchestratorComponent {
|
|
|
602
616
|
}
|
|
603
617
|
}
|
|
604
618
|
return true;
|
|
605
|
-
},
|
|
619
|
+
}, /* @ts-ignore */
|
|
620
|
+
...(ngDevMode ? [{ debugName: "currentPageValid" }] : /* istanbul ignore next */ []));
|
|
606
621
|
extendedFieldSignalContext = computed(() => ({
|
|
607
622
|
...this.fieldSignalContext(),
|
|
608
623
|
currentPageValid: this.currentPageValid,
|
|
609
|
-
}),
|
|
624
|
+
}), /* @ts-ignore */
|
|
625
|
+
...(ngDevMode ? [{ debugName: "extendedFieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
610
626
|
constructor() {
|
|
611
627
|
this.setupEventListeners();
|
|
612
628
|
explicitEffect([this.state, this.visiblePageIndices], ([state, visibleIndices]) => {
|
|
@@ -765,8 +781,8 @@ class PageOrchestratorComponent {
|
|
|
765
781
|
}
|
|
766
782
|
return false;
|
|
767
783
|
}
|
|
768
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
769
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
784
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
785
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: [
|
|
770
786
|
{
|
|
771
787
|
provide: FIELD_SIGNAL_CONTEXT,
|
|
772
788
|
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
@@ -805,9 +821,12 @@ class PageOrchestratorComponent {
|
|
|
805
821
|
}
|
|
806
822
|
}
|
|
807
823
|
}
|
|
808
|
-
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [
|
|
824
|
+
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [/* @ts-ignore */
|
|
825
|
+
import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs').then(m => m.default)], () => [/* @ts-ignore */
|
|
826
|
+
import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs').then(m => m.default)]] });
|
|
809
827
|
}
|
|
810
|
-
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "
|
|
828
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "22.0.0", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [/* @ts-ignore */
|
|
829
|
+
import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
811
830
|
type: Component,
|
|
812
831
|
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
813
832
|
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
@@ -867,8 +886,6 @@ function collectLeafFieldKeys(fields) {
|
|
|
867
886
|
return keys;
|
|
868
887
|
}
|
|
869
888
|
|
|
870
|
-
const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
|
|
871
|
-
|
|
872
889
|
const NORMALIZED_ARRAY_METADATA = Symbol('normalizedArrayMetadata');
|
|
873
890
|
function setNormalizedArrayMetadata(arrayField, metadata) {
|
|
874
891
|
arrayField[NORMALIZED_ARRAY_METADATA] = metadata;
|
|
@@ -1008,7 +1025,8 @@ function createSlot() {
|
|
|
1008
1025
|
let counter = 0;
|
|
1009
1026
|
const templates = new Map();
|
|
1010
1027
|
const itemOrder = [];
|
|
1011
|
-
const primitiveFieldKey = signal(undefined,
|
|
1028
|
+
const primitiveFieldKey = signal(undefined, /* @ts-ignore */
|
|
1029
|
+
...(ngDevMode ? [{ debugName: "primitiveFieldKey" }] : /* istanbul ignore next */ []));
|
|
1012
1030
|
return {
|
|
1013
1031
|
templates,
|
|
1014
1032
|
itemOrder,
|
|
@@ -1064,10 +1082,10 @@ class ArrayItemRegistryService {
|
|
|
1064
1082
|
slot.clear();
|
|
1065
1083
|
this.slots.clear();
|
|
1066
1084
|
}
|
|
1067
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1068
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1085
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1086
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService });
|
|
1069
1087
|
}
|
|
1070
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1088
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ArrayItemRegistryService, decorators: [{
|
|
1071
1089
|
type: Injectable
|
|
1072
1090
|
}] });
|
|
1073
1091
|
|
|
@@ -1091,10 +1109,10 @@ class SchemaRegistryService {
|
|
|
1091
1109
|
}
|
|
1092
1110
|
return schema;
|
|
1093
1111
|
}
|
|
1094
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1095
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1112
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1113
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService });
|
|
1096
1114
|
}
|
|
1097
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1115
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: SchemaRegistryService, decorators: [{
|
|
1098
1116
|
type: Injectable
|
|
1099
1117
|
}] });
|
|
1100
1118
|
|
|
@@ -1349,7 +1367,7 @@ function getNumberValidationConfig(fieldDef) {
|
|
|
1349
1367
|
}
|
|
1350
1368
|
function mapFieldToForm(fieldDef, fieldPath, inheritedContext = DEFAULT_FIELD_TREE_MAPPING_CONTEXT) {
|
|
1351
1369
|
const ownContext = resolveFieldOwnContext(fieldDef, inheritedContext);
|
|
1352
|
-
if (isPageField(fieldDef) || isRowField(fieldDef) ||
|
|
1370
|
+
if (isPageField(fieldDef) || isRowField(fieldDef) || isGenericContainerField(fieldDef)) {
|
|
1353
1371
|
const descendantContext = resolveDescendantContext(fieldDef, ownContext);
|
|
1354
1372
|
mapContainerChildren(fieldDef.fields, fieldPath, descendantContext);
|
|
1355
1373
|
return;
|
|
@@ -1371,7 +1389,7 @@ function mapContainerChildren(fields, parentPath, inheritedContext) {
|
|
|
1371
1389
|
return;
|
|
1372
1390
|
const pathRecord = parentPath;
|
|
1373
1391
|
for (const field of fields) {
|
|
1374
|
-
if (isPageField(field) || isRowField(field) ||
|
|
1392
|
+
if (isPageField(field) || isRowField(field) || isGenericContainerField(field)) {
|
|
1375
1393
|
const layoutOwn = resolveFieldOwnContext(field, inheritedContext);
|
|
1376
1394
|
const layoutDescendant = resolveDescendantContext(field, layoutOwn);
|
|
1377
1395
|
mapContainerChildren(field.fields, parentPath, layoutDescendant);
|
|
@@ -1492,7 +1510,7 @@ function mapArrayFieldToForm(arrayField, fieldPath, context) {
|
|
|
1492
1510
|
}
|
|
1493
1511
|
else if (isPageField(itemDef) ||
|
|
1494
1512
|
isRowField(itemDef) ||
|
|
1495
|
-
|
|
1513
|
+
isGenericContainerField(itemDef)) {
|
|
1496
1514
|
collectFieldsFromObjectItem([itemDef], allObjectFields);
|
|
1497
1515
|
}
|
|
1498
1516
|
else {
|
|
@@ -1508,7 +1526,7 @@ function mapArrayFieldToForm(arrayField, fieldPath, context) {
|
|
|
1508
1526
|
const itemSchema = schema((itemPath) => {
|
|
1509
1527
|
const pathRecord = itemPath;
|
|
1510
1528
|
for (const templateField of allObjectFields) {
|
|
1511
|
-
if (isRowField(templateField) || isPageField(templateField) ||
|
|
1529
|
+
if (isRowField(templateField) || isPageField(templateField) || isGenericContainerField(templateField)) {
|
|
1512
1530
|
const layoutOwn = resolveFieldOwnContext(templateField, context);
|
|
1513
1531
|
const layoutDescendant = resolveDescendantContext(templateField, layoutOwn);
|
|
1514
1532
|
mapContainerChildren(templateField.fields, itemPath, layoutDescendant);
|
|
@@ -1543,7 +1561,7 @@ function mapArrayFieldToForm(arrayField, fieldPath, context) {
|
|
|
1543
1561
|
function collectFieldsFromObjectItem(itemFields, allFields) {
|
|
1544
1562
|
const seenKeys = new Set(allFields.map((f) => f.key).filter(Boolean));
|
|
1545
1563
|
for (const field of itemFields) {
|
|
1546
|
-
if (isRowField(field) || isPageField(field) ||
|
|
1564
|
+
if (isRowField(field) || isPageField(field) || isGenericContainerField(field)) {
|
|
1547
1565
|
const containerKey = `__container_${field.type}_${JSON.stringify(field.fields?.map((f) => f.key))}`;
|
|
1548
1566
|
if (!seenKeys.has(containerKey)) {
|
|
1549
1567
|
seenKeys.add(containerKey);
|
|
@@ -2120,7 +2138,7 @@ function flattenFields(fields, registry, options = {}) {
|
|
|
2120
2138
|
let autoKeyCounter = 0;
|
|
2121
2139
|
for (const field of fields) {
|
|
2122
2140
|
const valueHandling = getFieldValueHandling(field.type, registry);
|
|
2123
|
-
if (options.preserveRows && (isRowField(field) ||
|
|
2141
|
+
if (options.preserveRows && (isRowField(field) || isGenericContainerField(field))) {
|
|
2124
2142
|
if (field.fields) {
|
|
2125
2143
|
const flattenedChildren = flattenFields(normalizeFieldsArray(field.fields), registry, options);
|
|
2126
2144
|
const autoPrefix = isRowField(field) ? 'auto_row' : 'auto_container';
|
|
@@ -2783,7 +2801,7 @@ function validateFormConfig(fields, registry, logger) {
|
|
|
2783
2801
|
}
|
|
2784
2802
|
}
|
|
2785
2803
|
|
|
2786
|
-
function validateFieldAddons(field, fieldRegistry,
|
|
2804
|
+
function validateFieldAddons(field, fieldRegistry, typeRegistry, source) {
|
|
2787
2805
|
const warnings = [];
|
|
2788
2806
|
const incoming = field.addons;
|
|
2789
2807
|
if (!incoming?.length) {
|
|
@@ -2801,8 +2819,8 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
|
|
|
2801
2819
|
return { addons: [], warnings };
|
|
2802
2820
|
}
|
|
2803
2821
|
const allowedSlots = addonSupport.slots;
|
|
2804
|
-
const
|
|
2805
|
-
const
|
|
2822
|
+
const allowedTypes = addonSupport.allowedTypes;
|
|
2823
|
+
const registeredTypes = Array.from(typeRegistry.keys());
|
|
2806
2824
|
const survivors = [];
|
|
2807
2825
|
for (const addon of incoming) {
|
|
2808
2826
|
if (!allowedSlots.includes(addon.slot)) {
|
|
@@ -2815,31 +2833,31 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
|
|
|
2815
2833
|
});
|
|
2816
2834
|
continue;
|
|
2817
2835
|
}
|
|
2818
|
-
const
|
|
2819
|
-
if (!
|
|
2836
|
+
const typeDef = typeRegistry.get(addon.type);
|
|
2837
|
+
if (!typeDef) {
|
|
2820
2838
|
warnings.push({
|
|
2821
|
-
type: 'unknown-
|
|
2839
|
+
type: 'unknown-type',
|
|
2822
2840
|
fieldKey: field.key,
|
|
2823
|
-
|
|
2824
|
-
|
|
2841
|
+
addonType: addon.type,
|
|
2842
|
+
registeredTypes,
|
|
2825
2843
|
});
|
|
2826
2844
|
continue;
|
|
2827
2845
|
}
|
|
2828
|
-
if (
|
|
2846
|
+
if (allowedTypes && !allowedTypes.includes(typeDef.type)) {
|
|
2829
2847
|
warnings.push({
|
|
2830
|
-
type: '
|
|
2848
|
+
type: 'type-not-allowed',
|
|
2831
2849
|
fieldKey: field.key,
|
|
2832
2850
|
fieldType,
|
|
2833
|
-
|
|
2834
|
-
|
|
2851
|
+
addonType: typeDef.type,
|
|
2852
|
+
allowedTypes,
|
|
2835
2853
|
});
|
|
2836
2854
|
continue;
|
|
2837
2855
|
}
|
|
2838
|
-
if (source === 'json' &&
|
|
2856
|
+
if (source === 'json' && typeDef.jsonSafe === false) {
|
|
2839
2857
|
warnings.push({
|
|
2840
|
-
type: 'code-only-
|
|
2858
|
+
type: 'code-only-type-in-json',
|
|
2841
2859
|
fieldKey: field.key,
|
|
2842
|
-
|
|
2860
|
+
addonType: typeDef.type,
|
|
2843
2861
|
});
|
|
2844
2862
|
continue;
|
|
2845
2863
|
}
|
|
@@ -2859,7 +2877,7 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
|
|
|
2859
2877
|
warnings.push({
|
|
2860
2878
|
type: 'code-only-action-in-json',
|
|
2861
2879
|
fieldKey: field.key,
|
|
2862
|
-
reason: `function '${key}' on
|
|
2880
|
+
reason: `function '${key}' on type '${typeDef.type}' (use a signal/observable in code, or omit in JSON)`,
|
|
2863
2881
|
});
|
|
2864
2882
|
}
|
|
2865
2883
|
};
|
|
@@ -2869,7 +2887,7 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
|
|
|
2869
2887
|
warnings.push({
|
|
2870
2888
|
type: 'code-only-action-in-json',
|
|
2871
2889
|
fieldKey: field.key,
|
|
2872
|
-
reason: `inline 'action' function on
|
|
2890
|
+
reason: `inline 'action' function on type '${typeDef.type}'`,
|
|
2873
2891
|
});
|
|
2874
2892
|
}
|
|
2875
2893
|
dropReactiveFn('hidden');
|
|
@@ -2883,22 +2901,22 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
|
|
|
2883
2901
|
warnings.push({
|
|
2884
2902
|
type: 'code-only-action-in-json',
|
|
2885
2903
|
fieldKey: field.key,
|
|
2886
|
-
reason: `multiple click variants on
|
|
2904
|
+
reason: `multiple click variants on type '${typeDef.type}' (${declaredVariants.join(', ')}); kept '${declaredVariants[0]}'`,
|
|
2887
2905
|
});
|
|
2888
2906
|
}
|
|
2889
2907
|
if (stripped) {
|
|
2890
2908
|
survivors.push(sanitized);
|
|
2891
2909
|
continue;
|
|
2892
2910
|
}
|
|
2893
|
-
if (
|
|
2911
|
+
if (typeDef.validate) {
|
|
2894
2912
|
try {
|
|
2895
|
-
|
|
2913
|
+
typeDef.validate(addon, field.key);
|
|
2896
2914
|
}
|
|
2897
2915
|
catch (error) {
|
|
2898
2916
|
warnings.push({
|
|
2899
2917
|
type: 'shape-violation',
|
|
2900
2918
|
fieldKey: field.key,
|
|
2901
|
-
|
|
2919
|
+
addonType: typeDef.type,
|
|
2902
2920
|
reason: error instanceof Error ? error.message : String(error),
|
|
2903
2921
|
});
|
|
2904
2922
|
continue;
|
|
@@ -2908,18 +2926,18 @@ function validateFieldAddons(field, fieldRegistry, kindRegistry, source) {
|
|
|
2908
2926
|
}
|
|
2909
2927
|
return { addons: survivors, warnings };
|
|
2910
2928
|
}
|
|
2911
|
-
function walkAndValidateAddons(fields, fieldRegistry,
|
|
2929
|
+
function walkAndValidateAddons(fields, fieldRegistry, typeRegistry, source) {
|
|
2912
2930
|
const allWarnings = [];
|
|
2913
2931
|
const sanitized = fields.map((field) => {
|
|
2914
2932
|
let next = field;
|
|
2915
2933
|
if (field.addons?.length) {
|
|
2916
|
-
const { addons, warnings } = validateFieldAddons(field, fieldRegistry,
|
|
2934
|
+
const { addons, warnings } = validateFieldAddons(field, fieldRegistry, typeRegistry, source);
|
|
2917
2935
|
allWarnings.push(...warnings);
|
|
2918
2936
|
next = { ...next, addons };
|
|
2919
2937
|
}
|
|
2920
2938
|
const children = next.fields;
|
|
2921
2939
|
if (Array.isArray(children)) {
|
|
2922
|
-
const { fields: visited, warnings } = walkAndValidateAddons(children, fieldRegistry,
|
|
2940
|
+
const { fields: visited, warnings } = walkAndValidateAddons(children, fieldRegistry, typeRegistry, source);
|
|
2923
2941
|
allWarnings.push(...warnings);
|
|
2924
2942
|
next = { ...next, fields: visited };
|
|
2925
2943
|
}
|
|
@@ -2936,14 +2954,14 @@ function addonWarningKey(w) {
|
|
|
2936
2954
|
return `${w.type}|${w.fieldKey}|${w.fieldType}`;
|
|
2937
2955
|
case 'unknown-slot':
|
|
2938
2956
|
return `${w.type}|${w.fieldKey}|${w.fieldType}|${w.slot}`;
|
|
2939
|
-
case 'unknown-
|
|
2940
|
-
return `${w.type}|${w.fieldKey}|${w.
|
|
2941
|
-
case '
|
|
2942
|
-
return `${w.type}|${w.fieldKey}|${w.fieldType}|${w.
|
|
2957
|
+
case 'unknown-type':
|
|
2958
|
+
return `${w.type}|${w.fieldKey}|${w.addonType}`;
|
|
2959
|
+
case 'type-not-allowed':
|
|
2960
|
+
return `${w.type}|${w.fieldKey}|${w.fieldType}|${w.addonType}`;
|
|
2943
2961
|
case 'shape-violation':
|
|
2944
|
-
return `${w.type}|${w.fieldKey}|${w.
|
|
2945
|
-
case 'code-only-
|
|
2946
|
-
return `${w.type}|${w.fieldKey}|${w.
|
|
2962
|
+
return `${w.type}|${w.fieldKey}|${w.addonType}|${w.reason}`;
|
|
2963
|
+
case 'code-only-type-in-json':
|
|
2964
|
+
return `${w.type}|${w.fieldKey}|${w.addonType}`;
|
|
2947
2965
|
case 'code-only-action-in-json':
|
|
2948
2966
|
return `${w.type}|${w.fieldKey}|${w.reason}`;
|
|
2949
2967
|
}
|
|
@@ -2958,17 +2976,17 @@ function formatAddonWarning(w) {
|
|
|
2958
2976
|
case 'unknown-slot':
|
|
2959
2977
|
return (`Field '${w.fieldKey}' (type: '${w.fieldType}'): slot '${w.slot}' is not supported. ` +
|
|
2960
2978
|
`Allowed: ${w.allowedSlots.join(', ')}. Addon dropped.`);
|
|
2961
|
-
case 'unknown-
|
|
2962
|
-
return (`Field '${w.fieldKey}': addon
|
|
2963
|
-
`Did you forget withCustomAddon({
|
|
2964
|
-
`Currently registered: ${w.
|
|
2965
|
-
case '
|
|
2966
|
-
return (`Field '${w.fieldKey}' (type: '${w.fieldType}'): addon
|
|
2967
|
-
`Allowed: ${w.
|
|
2979
|
+
case 'unknown-type':
|
|
2980
|
+
return (`Field '${w.fieldKey}': addon type '${w.addonType}' is not registered. ` +
|
|
2981
|
+
`Did you forget withCustomAddon({ type: '${w.addonType}', ... })? ` +
|
|
2982
|
+
`Currently registered: ${w.registeredTypes.join(', ') || '(none)'}. Addon dropped.`);
|
|
2983
|
+
case 'type-not-allowed':
|
|
2984
|
+
return (`Field '${w.fieldKey}' (type: '${w.fieldType}'): addon type '${w.addonType}' is not in the allowed list. ` +
|
|
2985
|
+
`Allowed: ${w.allowedTypes.join(', ')}. Addon dropped.`);
|
|
2968
2986
|
case 'shape-violation':
|
|
2969
|
-
return `Field '${w.fieldKey}': addon
|
|
2970
|
-
case 'code-only-
|
|
2971
|
-
return `Field '${w.fieldKey}': addon
|
|
2987
|
+
return `Field '${w.fieldKey}': addon type '${w.addonType}' shape invalid: ${w.reason}. Addon dropped.`;
|
|
2988
|
+
case 'code-only-type-in-json':
|
|
2989
|
+
return `Field '${w.fieldKey}': addon type '${w.addonType}' is code-only and cannot be used with JSON-derived configs. Addon dropped.`;
|
|
2972
2990
|
case 'code-only-action-in-json':
|
|
2973
2991
|
return `Field '${w.fieldKey}': ${w.reason}. Use 'preset' or 'actionRef' instead. Addon dropped.`;
|
|
2974
2992
|
}
|
|
@@ -3208,7 +3226,8 @@ class FormStateMachine {
|
|
|
3208
3226
|
}
|
|
3209
3227
|
constructor(config) {
|
|
3210
3228
|
this.config = config;
|
|
3211
|
-
this._state = signal(createUninitializedState(),
|
|
3229
|
+
this._state = signal(createUninitializedState(), /* @ts-ignore */
|
|
3230
|
+
...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
|
|
3212
3231
|
this.state = this._state.asReadonly();
|
|
3213
3232
|
this.state$ = toObservable(this._state, { injector: config.injector });
|
|
3214
3233
|
this.setupActionProcessing();
|
|
@@ -3518,12 +3537,13 @@ class FormStateManager {
|
|
|
3518
3537
|
})();
|
|
3519
3538
|
valueExclusionDefaults = inject(VALUE_EXCLUSION_DEFAULTS);
|
|
3520
3539
|
fieldRegistry = injectFieldRegistry();
|
|
3521
|
-
addonKindRegistry = inject(
|
|
3540
|
+
addonKindRegistry = inject(ADDON_TYPE_REGISTRY);
|
|
3522
3541
|
lastAddonWarningKeys = new Set();
|
|
3523
3542
|
scheduler = createSideEffectScheduler(this.injector);
|
|
3524
3543
|
machine;
|
|
3525
3544
|
fieldProcessors = inject(CONTAINER_FIELD_PROCESSORS);
|
|
3526
|
-
fieldLoadingErrors = signal([],
|
|
3545
|
+
fieldLoadingErrors = signal([], /* @ts-ignore */
|
|
3546
|
+
...(ngDevMode ? [{ debugName: "fieldLoadingErrors" }] : /* istanbul ignore next */ []));
|
|
3527
3547
|
_formCache = { current: undefined };
|
|
3528
3548
|
activeConfig = computed(() => {
|
|
3529
3549
|
const state = this.machine.state();
|
|
@@ -3540,33 +3560,39 @@ class FormStateManager {
|
|
|
3540
3560
|
if (state.type === LifecycleState.Initializing)
|
|
3541
3561
|
return state.config;
|
|
3542
3562
|
return undefined;
|
|
3543
|
-
},
|
|
3563
|
+
}, /* @ts-ignore */
|
|
3564
|
+
...(ngDevMode ? [{ debugName: "activeConfig" }] : /* istanbul ignore next */ []));
|
|
3544
3565
|
renderPhase = computed(() => {
|
|
3545
3566
|
const state = this.machine.state();
|
|
3546
3567
|
if (isTransitioningState(state) && state.phase === Phase.Teardown)
|
|
3547
3568
|
return Phase.Teardown;
|
|
3548
3569
|
return 'render';
|
|
3549
|
-
},
|
|
3550
|
-
|
|
3570
|
+
}, /* @ts-ignore */
|
|
3571
|
+
...(ngDevMode ? [{ debugName: "renderPhase" }] : /* istanbul ignore next */ []));
|
|
3572
|
+
shouldRender = computed(() => this.activeConfig() !== undefined, /* @ts-ignore */
|
|
3573
|
+
...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
3551
3574
|
formModeDetection = computed(() => {
|
|
3552
3575
|
const config = this.activeConfig();
|
|
3553
3576
|
if (!config) {
|
|
3554
3577
|
return { mode: 'non-paged', isValid: true, errors: [] };
|
|
3555
3578
|
}
|
|
3556
3579
|
return detectFormMode(config.fields || []);
|
|
3557
|
-
},
|
|
3580
|
+
}, /* @ts-ignore */
|
|
3581
|
+
...(ngDevMode ? [{ debugName: "formModeDetection" }] : /* istanbul ignore next */ []));
|
|
3558
3582
|
formConfigValidation = computed(() => {
|
|
3559
3583
|
const config = this.activeConfig();
|
|
3560
3584
|
if (!config)
|
|
3561
3585
|
return { isValid: true, errors: [], warnings: [] };
|
|
3562
3586
|
return FormModeValidator.validateFormConfiguration(config.fields || []);
|
|
3563
|
-
},
|
|
3587
|
+
}, /* @ts-ignore */
|
|
3588
|
+
...(ngDevMode ? [{ debugName: "formConfigValidation" }] : /* istanbul ignore next */ []));
|
|
3564
3589
|
effectiveFormOptions = computed(() => {
|
|
3565
3590
|
const config = this.activeConfig();
|
|
3566
3591
|
const configOptions = config?.options || {};
|
|
3567
3592
|
const inputOptions = this.deps.formOptions() ?? undefined;
|
|
3568
3593
|
return { ...configOptions, ...inputOptions };
|
|
3569
|
-
},
|
|
3594
|
+
}, /* @ts-ignore */
|
|
3595
|
+
...(ngDevMode ? [{ debugName: "effectiveFormOptions" }] : /* istanbul ignore next */ []));
|
|
3570
3596
|
pageFieldDefinitions = computed(() => {
|
|
3571
3597
|
const config = this.activeConfig();
|
|
3572
3598
|
const mode = this.formModeDetection().mode;
|
|
@@ -3574,8 +3600,10 @@ class FormStateManager {
|
|
|
3574
3600
|
return config.fields.filter(isPageField);
|
|
3575
3601
|
}
|
|
3576
3602
|
return [];
|
|
3577
|
-
},
|
|
3578
|
-
|
|
3603
|
+
}, /* @ts-ignore */
|
|
3604
|
+
...(ngDevMode ? [{ debugName: "pageFieldDefinitions" }] : /* istanbul ignore next */ []));
|
|
3605
|
+
rawFieldRegistry = computed(() => this.fieldRegistry.raw, /* @ts-ignore */
|
|
3606
|
+
...(ngDevMode ? [{ debugName: "rawFieldRegistry" }] : /* istanbul ignore next */ []));
|
|
3579
3607
|
formSetup = computed(() => {
|
|
3580
3608
|
const registry = this.rawFieldRegistry();
|
|
3581
3609
|
const state = this.machine.state();
|
|
@@ -3600,14 +3628,17 @@ class FormStateManager {
|
|
|
3600
3628
|
return this.createFormSetupFromConfig(config.fields, modeDetection.mode, registry);
|
|
3601
3629
|
}
|
|
3602
3630
|
return this.createEmptyFormSetup(registry);
|
|
3603
|
-
},
|
|
3604
|
-
|
|
3631
|
+
}, /* @ts-ignore */
|
|
3632
|
+
...(ngDevMode ? [{ debugName: "formSetup" }] : /* istanbul ignore next */ []));
|
|
3633
|
+
defaultValues = linkedSignal(() => this.formSetup().defaultValues, /* @ts-ignore */
|
|
3634
|
+
...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
|
|
3605
3635
|
validKeys = computed(() => {
|
|
3606
3636
|
const schemaFields = this.formSetup().schemaFields;
|
|
3607
3637
|
if (!schemaFields || schemaFields.length === 0)
|
|
3608
3638
|
return undefined;
|
|
3609
3639
|
return new Set(schemaFields.map((f) => f.key).filter((key) => key !== undefined));
|
|
3610
|
-
},
|
|
3640
|
+
}, /* @ts-ignore */
|
|
3641
|
+
...(ngDevMode ? [{ debugName: "validKeys" }] : /* istanbul ignore next */ []));
|
|
3611
3642
|
fieldStateSnapshot = computed(() => {
|
|
3612
3643
|
const setup = this.formSetup();
|
|
3613
3644
|
if (!setup.schemaFields || setup.schemaFields.length === 0)
|
|
@@ -3615,9 +3646,12 @@ class FormStateManager {
|
|
|
3615
3646
|
const snapshot = {};
|
|
3616
3647
|
collectFieldStateSnapshot(setup.schemaFields, asFieldTreeRecord(this.form()), [], snapshot);
|
|
3617
3648
|
return snapshot;
|
|
3618
|
-
},
|
|
3619
|
-
|
|
3620
|
-
|
|
3649
|
+
}, /* @ts-ignore */
|
|
3650
|
+
...(ngDevMode ? [{ debugName: "fieldStateSnapshot" }] : /* istanbul ignore next */ []));
|
|
3651
|
+
excludedValueStore = signal({}, /* @ts-ignore */
|
|
3652
|
+
...(ngDevMode ? [{ debugName: "excludedValueStore" }] : /* istanbul ignore next */ []));
|
|
3653
|
+
prevFieldStateSnapshot = signal({}, /* @ts-ignore */
|
|
3654
|
+
...(ngDevMode ? [{ debugName: "prevFieldStateSnapshot" }] : /* istanbul ignore next */ []));
|
|
3621
3655
|
entity = linkedSignal(() => {
|
|
3622
3656
|
const inputValue = this.deps.value();
|
|
3623
3657
|
const defaults = this.defaultValues();
|
|
@@ -3656,12 +3690,14 @@ class FormStateManager {
|
|
|
3656
3690
|
return createFormLevelSchema(config.schema);
|
|
3657
3691
|
}
|
|
3658
3692
|
return undefined;
|
|
3659
|
-
}),
|
|
3693
|
+
}), /* @ts-ignore */
|
|
3694
|
+
...(ngDevMode ? [{ debugName: "formSchema" }] : /* istanbul ignore next */ []));
|
|
3660
3695
|
form = computed(() => {
|
|
3661
3696
|
const schema = this.formSchema();
|
|
3662
3697
|
const injector = this.injector;
|
|
3663
3698
|
return untracked(() => (schema ? form(this.entity, schema, { injector }) : form(this.entity, { injector })));
|
|
3664
|
-
},
|
|
3699
|
+
}, /* @ts-ignore */
|
|
3700
|
+
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
3665
3701
|
isFieldPipelineSettled;
|
|
3666
3702
|
fieldSignalContext = computed(() => {
|
|
3667
3703
|
const formSignal = this.form;
|
|
@@ -3681,9 +3717,12 @@ class FormStateManager {
|
|
|
3681
3717
|
return currentForm;
|
|
3682
3718
|
},
|
|
3683
3719
|
};
|
|
3684
|
-
},
|
|
3685
|
-
|
|
3686
|
-
|
|
3720
|
+
}, /* @ts-ignore */
|
|
3721
|
+
...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : /* istanbul ignore next */ []));
|
|
3722
|
+
formInstance = computed(() => this.form()(), /* @ts-ignore */
|
|
3723
|
+
...(ngDevMode ? [{ debugName: "formInstance" }] : /* istanbul ignore next */ []));
|
|
3724
|
+
formValue = computed(() => this.formInstance().value(), /* @ts-ignore */
|
|
3725
|
+
...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
|
|
3687
3726
|
filteredFormValue = computed(() => {
|
|
3688
3727
|
const rawValue = this.formValue();
|
|
3689
3728
|
const setup = this.formSetup();
|
|
@@ -3699,7 +3738,8 @@ class FormStateManager {
|
|
|
3699
3738
|
};
|
|
3700
3739
|
const fieldTreeRecord = asFieldTreeRecord(formTree);
|
|
3701
3740
|
return filterFormValue(rawValue, setup.schemaFields, fieldTreeRecord, setup.registry, this.valueExclusionDefaults, formOptions);
|
|
3702
|
-
},
|
|
3741
|
+
}, /* @ts-ignore */
|
|
3742
|
+
...(ngDevMode ? [{ debugName: "filteredFormValue" }] : /* istanbul ignore next */ []));
|
|
3703
3743
|
boundFormValue = computed(() => {
|
|
3704
3744
|
const rawValue = this.formValue();
|
|
3705
3745
|
const setup = this.formSetup();
|
|
@@ -3715,18 +3755,26 @@ class FormStateManager {
|
|
|
3715
3755
|
};
|
|
3716
3756
|
const fieldTreeRecord = asFieldTreeRecord(formTree);
|
|
3717
3757
|
return filterFormValue(rawValue, setup.schemaFields, fieldTreeRecord, setup.registry, BOUND_VALUE_EXCLUSION_BASELINE, formOptions);
|
|
3718
|
-
},
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3758
|
+
}, /* @ts-ignore */
|
|
3759
|
+
...(ngDevMode ? [{ debugName: "boundFormValue" }] : /* istanbul ignore next */ []));
|
|
3760
|
+
valid = computed(() => this.formInstance().valid(), /* @ts-ignore */
|
|
3761
|
+
...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
|
|
3762
|
+
invalid = computed(() => this.formInstance().invalid(), /* @ts-ignore */
|
|
3763
|
+
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
3764
|
+
dirty = computed(() => this.formInstance().dirty(), /* @ts-ignore */
|
|
3765
|
+
...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
|
|
3766
|
+
touched = computed(() => this.formInstance().touched(), /* @ts-ignore */
|
|
3767
|
+
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
3768
|
+
errors = computed(() => this.formInstance().errors(), /* @ts-ignore */
|
|
3769
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
3724
3770
|
disabled = computed(() => {
|
|
3725
3771
|
const optionsDisabled = this.effectiveFormOptions().disabled;
|
|
3726
3772
|
const formDisabled = this.formInstance().disabled();
|
|
3727
3773
|
return optionsDisabled ?? formDisabled;
|
|
3728
|
-
},
|
|
3729
|
-
|
|
3774
|
+
}, /* @ts-ignore */
|
|
3775
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
3776
|
+
submitting = computed(() => this.formInstance().submitting(), /* @ts-ignore */
|
|
3777
|
+
...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
|
|
3730
3778
|
fieldsSource = computed(() => {
|
|
3731
3779
|
const state = this.machine.state();
|
|
3732
3780
|
if (isTransitioningState(state)) {
|
|
@@ -3751,7 +3799,8 @@ class FormStateManager {
|
|
|
3751
3799
|
fieldInjector = computed(() => Injector.create({
|
|
3752
3800
|
parent: this.injector,
|
|
3753
3801
|
providers: [{ provide: FIELD_SIGNAL_CONTEXT, useValue: this.fieldSignalContext() }],
|
|
3754
|
-
}),
|
|
3802
|
+
}), /* @ts-ignore */
|
|
3803
|
+
...(ngDevMode ? [{ debugName: "fieldInjector" }] : /* istanbul ignore next */ []));
|
|
3755
3804
|
resolvedFields;
|
|
3756
3805
|
submitted$;
|
|
3757
3806
|
reset$;
|
|
@@ -3817,7 +3866,8 @@ class FormStateManager {
|
|
|
3817
3866
|
}), map((fields) => fields.filter((f) => f !== undefined)), scan(reconcileFields, [])), { initialValue: [], injector: this.injector });
|
|
3818
3867
|
this.isFieldPipelineSettled = computed(() => {
|
|
3819
3868
|
return this.fieldsSource().length === this.resolvedFields().length;
|
|
3820
|
-
},
|
|
3869
|
+
}, /* @ts-ignore */
|
|
3870
|
+
...(ngDevMode ? [{ debugName: "isFieldPipelineSettled" }] : /* istanbul ignore next */ []));
|
|
3821
3871
|
this.submitted$ = this.eventBus.on('submit').pipe(filter(() => {
|
|
3822
3872
|
if (!this.valid()) {
|
|
3823
3873
|
this.logger.debug('Form submitted while invalid, not emitting to (submitted) output');
|
|
@@ -3894,7 +3944,7 @@ class FormStateManager {
|
|
|
3894
3944
|
this.deps.value.set(emptyValue);
|
|
3895
3945
|
}
|
|
3896
3946
|
submit() {
|
|
3897
|
-
this.eventBus.dispatch(
|
|
3947
|
+
this.eventBus.dispatch(FormSubmitEvent);
|
|
3898
3948
|
}
|
|
3899
3949
|
setupEffects() {
|
|
3900
3950
|
explicitEffect([this.deps.config], ([config]) => {
|
|
@@ -4038,17 +4088,115 @@ class FormStateManager {
|
|
|
4038
4088
|
ngOnDestroy() {
|
|
4039
4089
|
this.machine.dispatch({ type: Action.Destroy });
|
|
4040
4090
|
}
|
|
4041
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
4042
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
4091
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4092
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager });
|
|
4043
4093
|
}
|
|
4044
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4094
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormStateManager, decorators: [{
|
|
4045
4095
|
type: Injectable
|
|
4046
4096
|
}], ctorParameters: () => [] });
|
|
4047
4097
|
|
|
4098
|
+
function traverseFieldsWithContext(fields, context, visitor, hooks) {
|
|
4099
|
+
for (const field of fields) {
|
|
4100
|
+
visitor(field, context);
|
|
4101
|
+
if (!hasChildFields(field))
|
|
4102
|
+
continue;
|
|
4103
|
+
if (field.type === 'array') {
|
|
4104
|
+
const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
|
|
4105
|
+
const arrayChildren = collectArrayChildren(field);
|
|
4106
|
+
traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
|
|
4107
|
+
continue;
|
|
4108
|
+
}
|
|
4109
|
+
const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
|
|
4110
|
+
const childContext = mergeContext(context, overrides);
|
|
4111
|
+
traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
function someField(fields, predicate) {
|
|
4115
|
+
return fields.some((field) => {
|
|
4116
|
+
if (predicate(field))
|
|
4117
|
+
return true;
|
|
4118
|
+
if (!hasChildFields(field))
|
|
4119
|
+
return false;
|
|
4120
|
+
const children = field.type === 'array' ? collectArrayChildren(field) : normalizeFieldsArray(field.fields);
|
|
4121
|
+
return someField(children, predicate);
|
|
4122
|
+
});
|
|
4123
|
+
}
|
|
4124
|
+
function collectArrayChildren(field) {
|
|
4125
|
+
let arrayItems = normalizeFieldsArray(field.fields);
|
|
4126
|
+
if (arrayItems.length === 0) {
|
|
4127
|
+
const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
|
|
4128
|
+
if (metadataTemplate) {
|
|
4129
|
+
arrayItems = [
|
|
4130
|
+
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
4131
|
+
];
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
const flattened = [];
|
|
4135
|
+
for (const item of arrayItems) {
|
|
4136
|
+
if (Array.isArray(item)) {
|
|
4137
|
+
flattened.push(...item);
|
|
4138
|
+
}
|
|
4139
|
+
else {
|
|
4140
|
+
flattened.push(item);
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
return flattened;
|
|
4144
|
+
}
|
|
4145
|
+
function mergeContext(base, overrides) {
|
|
4146
|
+
return overrides ? { ...base, ...overrides } : { ...base };
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
function configHasDerivations(fields) {
|
|
4150
|
+
return someField(fields, (field) => {
|
|
4151
|
+
if (!field.key)
|
|
4152
|
+
return false;
|
|
4153
|
+
const validationField = field;
|
|
4154
|
+
return !!validationField.derivation || !!validationField.logic?.some(isDerivationLogicConfig);
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
const DERIVATION_RENDER_GATE = new InjectionToken('DERIVATION_RENDER_GATE');
|
|
4159
|
+
function createDerivationRenderGate() {
|
|
4160
|
+
const stateManager = inject(FormStateManager);
|
|
4161
|
+
const injector = inject(Injector);
|
|
4162
|
+
const pendingTasks = inject(PendingTasks);
|
|
4163
|
+
const logger = inject(DynamicFormLogger);
|
|
4164
|
+
const hasDerivations = computed(() => {
|
|
4165
|
+
const setup = stateManager.formSetup();
|
|
4166
|
+
return !!setup && configHasDerivations(setup.schemaFields);
|
|
4167
|
+
}, /* @ts-ignore */
|
|
4168
|
+
...(ngDevMode ? [{ debugName: "hasDerivations" }] : /* istanbul ignore next */ []));
|
|
4169
|
+
const engine = signal('idle', /* @ts-ignore */
|
|
4170
|
+
...(ngDevMode ? [{ debugName: "engine" }] : /* istanbul ignore next */ []));
|
|
4171
|
+
explicitEffect([hasDerivations], ([has]) => {
|
|
4172
|
+
if (!has || engine() !== 'idle')
|
|
4173
|
+
return;
|
|
4174
|
+
engine.set('loading');
|
|
4175
|
+
pendingTasks.run(() => import('./ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-C8kpF0tw.mjs')
|
|
4176
|
+
.then(({ bootstrapDerivationOrchestrator }) => {
|
|
4177
|
+
runInInjectionContext(injector, () => bootstrapDerivationOrchestrator());
|
|
4178
|
+
engine.set('wired');
|
|
4179
|
+
})
|
|
4180
|
+
.catch((error) => {
|
|
4181
|
+
engine.set('failed');
|
|
4182
|
+
logger.error('[Dynamic Forms] Failed to load the derivation engine; derivations will not apply.', error);
|
|
4183
|
+
}));
|
|
4184
|
+
});
|
|
4185
|
+
return computed(() => {
|
|
4186
|
+
if (!hasDerivations())
|
|
4187
|
+
return true;
|
|
4188
|
+
const state = engine();
|
|
4189
|
+
return state === 'wired' || state === 'failed';
|
|
4190
|
+
});
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
const FORM_INITIALIZER = new InjectionToken('FORM_INITIALIZER');
|
|
4194
|
+
|
|
4048
4195
|
const AUTO_ID_PREFIX = 'df-';
|
|
4049
4196
|
class DynamicFormInstanceRegistry {
|
|
4050
4197
|
slots = new Map();
|
|
4051
|
-
_membership = signal(0,
|
|
4198
|
+
_membership = signal(0, /* @ts-ignore */
|
|
4199
|
+
...(ngDevMode ? [{ debugName: "_membership" }] : /* istanbul ignore next */ []));
|
|
4052
4200
|
multiplePresent = computed(() => {
|
|
4053
4201
|
this._membership();
|
|
4054
4202
|
let visible = 0;
|
|
@@ -4060,7 +4208,8 @@ class DynamicFormInstanceRegistry {
|
|
|
4060
4208
|
}
|
|
4061
4209
|
}
|
|
4062
4210
|
return false;
|
|
4063
|
-
},
|
|
4211
|
+
}, /* @ts-ignore */
|
|
4212
|
+
...(ngDevMode ? [{ debugName: "multiplePresent" }] : /* istanbul ignore next */ []));
|
|
4064
4213
|
register(visible) {
|
|
4065
4214
|
let slot = 1;
|
|
4066
4215
|
while (this.slots.has(slot))
|
|
@@ -4073,10 +4222,10 @@ class DynamicFormInstanceRegistry {
|
|
|
4073
4222
|
this.slots.delete(Number(id.slice(AUTO_ID_PREFIX.length)));
|
|
4074
4223
|
this._membership.update((m) => m + 1);
|
|
4075
4224
|
}
|
|
4076
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
4077
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
4225
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4226
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, providedIn: 'root' });
|
|
4078
4227
|
}
|
|
4079
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicFormInstanceRegistry, decorators: [{
|
|
4080
4229
|
type: Injectable,
|
|
4081
4230
|
args: [{ providedIn: 'root' }]
|
|
4082
4231
|
}] });
|
|
@@ -4098,15 +4247,18 @@ class FormIdPrefixService {
|
|
|
4098
4247
|
options = inject(FORM_OPTIONS, { optional: true });
|
|
4099
4248
|
logger = inject(DynamicFormLogger, { optional: true });
|
|
4100
4249
|
host = inject((ElementRef)).nativeElement;
|
|
4101
|
-
visible = signal(true,
|
|
4250
|
+
visible = signal(true, /* @ts-ignore */
|
|
4251
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
4102
4252
|
autoId = this.registry.register(this.visible);
|
|
4103
|
-
rawPrefix = computed(() => this.options?.()?.idPrefix?.trim() || undefined,
|
|
4253
|
+
rawPrefix = computed(() => this.options?.()?.idPrefix?.trim() || undefined, /* @ts-ignore */
|
|
4254
|
+
...(ngDevMode ? [{ debugName: "rawPrefix" }] : /* istanbul ignore next */ []));
|
|
4104
4255
|
prefix = computed(() => {
|
|
4105
4256
|
const explicit = this.rawPrefix();
|
|
4106
4257
|
if (explicit)
|
|
4107
4258
|
return sanitizeIdPrefix(explicit);
|
|
4108
4259
|
return this.registry.multiplePresent() ? this.autoId : '';
|
|
4109
|
-
},
|
|
4260
|
+
}, /* @ts-ignore */
|
|
4261
|
+
...(ngDevMode ? [{ debugName: "prefix" }] : /* istanbul ignore next */ []));
|
|
4110
4262
|
constructor() {
|
|
4111
4263
|
const destroyRef = inject(DestroyRef);
|
|
4112
4264
|
destroyRef.onDestroy(() => this.registry.unregister(this.autoId));
|
|
@@ -4124,10 +4276,10 @@ class FormIdPrefixService {
|
|
|
4124
4276
|
});
|
|
4125
4277
|
}
|
|
4126
4278
|
}
|
|
4127
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
4128
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
4279
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4280
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService });
|
|
4129
4281
|
}
|
|
4130
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4282
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: FormIdPrefixService, decorators: [{
|
|
4131
4283
|
type: Injectable
|
|
4132
4284
|
}], ctorParameters: () => [] });
|
|
4133
4285
|
|
|
@@ -4136,2588 +4288,146 @@ const DERIVATION_WARNING_TRACKER = new InjectionToken('DerivationWarningTracker'
|
|
|
4136
4288
|
factory: createWarningTracker,
|
|
4137
4289
|
});
|
|
4138
4290
|
|
|
4139
|
-
function
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
}
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
}
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4291
|
+
function coreProviders() {
|
|
4292
|
+
return [
|
|
4293
|
+
EventBus,
|
|
4294
|
+
{ provide: CONTAINER_FIELD_PROCESSORS, useFactory: createContainerFieldProcessors },
|
|
4295
|
+
SchemaRegistryService,
|
|
4296
|
+
FunctionRegistryService,
|
|
4297
|
+
{
|
|
4298
|
+
provide: FORM_STATE_DEPS,
|
|
4299
|
+
useFactory: () => ({ config: null, formOptions: null, value: null, source: null }),
|
|
4300
|
+
},
|
|
4301
|
+
FormStateManager,
|
|
4302
|
+
{
|
|
4303
|
+
provide: RootFormRegistryService,
|
|
4304
|
+
useFactory: (stateManager) => new RootFormRegistryService(stateManager.formValue, computed(() => stateManager.form())),
|
|
4305
|
+
deps: [FormStateManager],
|
|
4306
|
+
},
|
|
4307
|
+
FieldContextRegistryService,
|
|
4308
|
+
ArrayItemRegistryService,
|
|
4309
|
+
{
|
|
4310
|
+
provide: DEFAULT_PROPS,
|
|
4311
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultProps),
|
|
4312
|
+
deps: [FormStateManager],
|
|
4313
|
+
},
|
|
4314
|
+
{
|
|
4315
|
+
provide: DEFAULT_WRAPPERS,
|
|
4316
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultWrappers),
|
|
4317
|
+
deps: [FormStateManager],
|
|
4318
|
+
},
|
|
4319
|
+
{
|
|
4320
|
+
provide: DEFAULT_VALIDATION_MESSAGES,
|
|
4321
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultValidationMessages),
|
|
4322
|
+
deps: [FormStateManager],
|
|
4323
|
+
},
|
|
4324
|
+
{
|
|
4325
|
+
provide: FORM_OPTIONS,
|
|
4326
|
+
useFactory: (stateManager) => stateManager.effectiveFormOptions,
|
|
4327
|
+
deps: [FormStateManager],
|
|
4328
|
+
},
|
|
4329
|
+
{
|
|
4330
|
+
provide: EXTERNAL_DATA,
|
|
4331
|
+
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.externalData),
|
|
4332
|
+
deps: [FormStateManager],
|
|
4333
|
+
},
|
|
4334
|
+
FormIdPrefixService,
|
|
4335
|
+
{
|
|
4336
|
+
provide: FORM_ID_PREFIX,
|
|
4337
|
+
useFactory: (svc) => svc.prefix,
|
|
4338
|
+
deps: [FormIdPrefixService],
|
|
4339
|
+
},
|
|
4340
|
+
{ provide: DEPRECATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
4341
|
+
{ provide: ADDON_ACTION_REGISTRY, useFactory: createAddonActionRegistry },
|
|
4342
|
+
];
|
|
4167
4343
|
}
|
|
4168
|
-
function
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
.split('.')
|
|
4174
|
-
.filter(Boolean);
|
|
4175
|
-
}
|
|
4176
|
-
function joinPath(segments) {
|
|
4177
|
-
return segments.join('.');
|
|
4178
|
-
}
|
|
4179
|
-
function getParentPath(path) {
|
|
4180
|
-
const segments = splitPath(path);
|
|
4181
|
-
if (segments.length <= 1)
|
|
4182
|
-
return '';
|
|
4183
|
-
return joinPath(segments.slice(0, -1));
|
|
4184
|
-
}
|
|
4185
|
-
function getLeafPath(path) {
|
|
4186
|
-
const segments = splitPath(path);
|
|
4187
|
-
return segments[segments.length - 1] ?? '';
|
|
4188
|
-
}
|
|
4189
|
-
function parseMultiArrayPath(path) {
|
|
4190
|
-
const parts = path.split('.');
|
|
4191
|
-
const segments = [];
|
|
4192
|
-
const placeholderPositions = [];
|
|
4193
|
-
for (let i = 0; i < parts.length; i++) {
|
|
4194
|
-
if (parts[i] === '$') {
|
|
4195
|
-
placeholderPositions.push(i);
|
|
4196
|
-
}
|
|
4197
|
-
else {
|
|
4198
|
-
segments.push(parts[i]);
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
return {
|
|
4202
|
-
isArrayPath: placeholderPositions.length > 0,
|
|
4203
|
-
placeholderCount: placeholderPositions.length,
|
|
4204
|
-
segments,
|
|
4205
|
-
placeholderPositions,
|
|
4206
|
-
};
|
|
4344
|
+
function derivationProviders() {
|
|
4345
|
+
return [
|
|
4346
|
+
{ provide: DERIVATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
4347
|
+
{ provide: DERIVATION_RENDER_GATE, useFactory: createDerivationRenderGate },
|
|
4348
|
+
];
|
|
4207
4349
|
}
|
|
4208
|
-
function
|
|
4209
|
-
|
|
4210
|
-
let indexPointer = 0;
|
|
4211
|
-
const resolved = parts.map((part) => {
|
|
4212
|
-
if (part === '$') {
|
|
4213
|
-
if (indexPointer >= indices.length) {
|
|
4214
|
-
throw new Error(`Not enough indices provided for path '${path}'. ` +
|
|
4215
|
-
`Expected ${parts.filter((p) => p === '$').length} indices, got ${indices.length}.`);
|
|
4216
|
-
}
|
|
4217
|
-
return String(indices[indexPointer++]);
|
|
4218
|
-
}
|
|
4219
|
-
return part;
|
|
4220
|
-
});
|
|
4221
|
-
if (indexPointer < indices.length) {
|
|
4222
|
-
throw new Error(`Too many indices provided for path '${path}'. ` + `Expected ${indexPointer} indices, got ${indices.length}.`);
|
|
4223
|
-
}
|
|
4224
|
-
return resolved.join('.');
|
|
4350
|
+
function propertyDerivationProviders() {
|
|
4351
|
+
return [{ provide: PROPERTY_OVERRIDE_STORE, useFactory: createPropertyOverrideStore }];
|
|
4225
4352
|
}
|
|
4226
|
-
function
|
|
4227
|
-
return
|
|
4353
|
+
function httpExpressionProviders() {
|
|
4354
|
+
return [{ provide: HTTP_CONDITION_CACHE, useFactory: () => new HttpConditionCache(100) }, HttpConditionFunctionCacheService];
|
|
4228
4355
|
}
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
return { entries: [] };
|
|
4356
|
+
function asyncExpressionProviders() {
|
|
4357
|
+
return [AsyncConditionFunctionCacheService];
|
|
4232
4358
|
}
|
|
4233
|
-
function
|
|
4234
|
-
return
|
|
4235
|
-
appliedDerivations: new Set(),
|
|
4236
|
-
iteration: 0,
|
|
4237
|
-
};
|
|
4359
|
+
function dynamicValueProviders() {
|
|
4360
|
+
return [DynamicValueFunctionCacheService];
|
|
4238
4361
|
}
|
|
4239
|
-
function
|
|
4240
|
-
return
|
|
4362
|
+
function logicProviders() {
|
|
4363
|
+
return [LogicFunctionCacheService];
|
|
4241
4364
|
}
|
|
4242
|
-
function
|
|
4243
|
-
return
|
|
4365
|
+
function provideDynamicFormDI() {
|
|
4366
|
+
return [
|
|
4367
|
+
...coreProviders(),
|
|
4368
|
+
...logicProviders(),
|
|
4369
|
+
...dynamicValueProviders(),
|
|
4370
|
+
...httpExpressionProviders(),
|
|
4371
|
+
...asyncExpressionProviders(),
|
|
4372
|
+
...propertyDerivationProviders(),
|
|
4373
|
+
...derivationProviders(),
|
|
4374
|
+
];
|
|
4244
4375
|
}
|
|
4245
4376
|
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
return typeof candidate.then === 'function' || typeof candidate.subscribe === 'function';
|
|
4251
|
-
}
|
|
4252
|
-
function computeValueFromEntry(entry, evalContext, options) {
|
|
4253
|
-
if (entry.value !== undefined) {
|
|
4254
|
-
return entry.value;
|
|
4377
|
+
class EventDispatcher {
|
|
4378
|
+
bus;
|
|
4379
|
+
dispatch(event) {
|
|
4380
|
+
this.bus?.emitInstance(event);
|
|
4255
4381
|
}
|
|
4256
|
-
|
|
4257
|
-
|
|
4382
|
+
connect(bus) {
|
|
4383
|
+
this.bus = bus;
|
|
4258
4384
|
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
evalContext.logger.warn(`Both "fn" and "functionName" are set on derivation for '${options.subject}'. Inline "fn" takes precedence; "functionName" is ignored.`);
|
|
4262
|
-
}
|
|
4263
|
-
const fn = entry.fn ?? (entry.functionName ? options.derivationFunctions?.[entry.functionName] : undefined);
|
|
4264
|
-
if (!fn) {
|
|
4265
|
-
const kind = options.functionKind ?? 'Derivation function';
|
|
4266
|
-
throw new DynamicFormError(`${kind} '${entry.functionName}' not found in customFnConfig.derivations`);
|
|
4267
|
-
}
|
|
4268
|
-
const result = fn(evalContext);
|
|
4269
|
-
if (isThenableOrObservable(result)) {
|
|
4270
|
-
evalContext.logger.warn(`Derivation for '${options.subject}' returned a Promise or Observable. ` +
|
|
4271
|
-
`Synchronous derivations must return a value; use 'asyncDerivations' or an HTTP derivation for asynchronous logic. Skipping.`);
|
|
4272
|
-
return undefined;
|
|
4273
|
-
}
|
|
4274
|
-
return result;
|
|
4385
|
+
disconnect() {
|
|
4386
|
+
this.bus = undefined;
|
|
4275
4387
|
}
|
|
4276
|
-
|
|
4388
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4389
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher });
|
|
4277
4390
|
}
|
|
4391
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: EventDispatcher, decorators: [{
|
|
4392
|
+
type: Injectable
|
|
4393
|
+
}] });
|
|
4278
4394
|
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
}
|
|
4285
|
-
function resolveArrayItemScope(pathInfo, arrayItem) {
|
|
4286
|
-
const relativePath = pathInfo.isArrayPath ? pathInfo.relativePath : '';
|
|
4287
|
-
const innerParentPath = relativePath.includes('.') ? relativePath.slice(0, relativePath.lastIndexOf('.')) : '';
|
|
4288
|
-
const groupValue = innerParentPath ? getNestedValue(arrayItem, innerParentPath) : arrayItem;
|
|
4289
|
-
const fieldValue = relativePath ? getNestedValue(arrayItem, relativePath) : arrayItem;
|
|
4290
|
-
return { relativePath, groupValue, fieldValue };
|
|
4291
|
-
}
|
|
4292
|
-
|
|
4293
|
-
const MAX_DERIVATION_ITERATIONS = 10;
|
|
4294
|
-
const ARRAY_PLACEHOLDER = '.$.';
|
|
4295
|
-
const MAX_AST_CACHE_SIZE = 1000;
|
|
4296
|
-
const DERIVATION_KEY_DELIMITER = '\x00';
|
|
4297
|
-
|
|
4298
|
-
function resolveFieldInstance(rootForm, fieldPath) {
|
|
4299
|
-
const parts = fieldPath.split('.');
|
|
4300
|
-
let current = rootForm;
|
|
4301
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
4302
|
-
const next = current[parts[i]];
|
|
4303
|
-
if (!next)
|
|
4304
|
-
return undefined;
|
|
4305
|
-
current = next;
|
|
4306
|
-
}
|
|
4307
|
-
const fieldAccessor = current[parts[parts.length - 1]];
|
|
4308
|
-
if (!fieldAccessor)
|
|
4309
|
-
return undefined;
|
|
4310
|
-
return untracked(fieldAccessor);
|
|
4311
|
-
}
|
|
4312
|
-
function readFieldDirty(rootForm, fieldPath) {
|
|
4313
|
-
const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
|
|
4314
|
-
if (!fieldInstance)
|
|
4315
|
-
return undefined;
|
|
4316
|
-
return untracked(fieldInstance.dirty);
|
|
4317
|
-
}
|
|
4318
|
-
function resetFieldState(rootForm, fieldPath) {
|
|
4319
|
-
const fieldInstance = resolveFieldInstance(rootForm, fieldPath);
|
|
4320
|
-
if (!fieldInstance)
|
|
4321
|
-
return;
|
|
4322
|
-
fieldInstance.reset();
|
|
4323
|
-
}
|
|
4324
|
-
function applyValueToForm(targetPath, value, rootForm, logger, warningTracker) {
|
|
4325
|
-
if (!targetPath.includes('.')) {
|
|
4326
|
-
return setFieldValue(rootForm, targetPath, value, logger, warningTracker);
|
|
4327
|
-
}
|
|
4328
|
-
const parts = targetPath.split('.');
|
|
4329
|
-
let current = rootForm;
|
|
4330
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
4331
|
-
const part = parts[i];
|
|
4332
|
-
if (part === '$') {
|
|
4333
|
-
return false;
|
|
4334
|
-
}
|
|
4335
|
-
const next = current[part];
|
|
4336
|
-
if (!next) {
|
|
4337
|
-
warnMissingField(targetPath, logger, warningTracker);
|
|
4338
|
-
return false; }
|
|
4339
|
-
current = next;
|
|
4340
|
-
}
|
|
4341
|
-
const finalPart = parts[parts.length - 1];
|
|
4342
|
-
return setFieldValue(current, finalPart, value, logger, warningTracker);
|
|
4343
|
-
}
|
|
4344
|
-
function setFieldValue(parent, fieldKey, value, logger, warningTracker) {
|
|
4345
|
-
const fieldAccessor = parent[fieldKey];
|
|
4346
|
-
if (!fieldAccessor) {
|
|
4347
|
-
warnMissingField(fieldKey, logger, warningTracker);
|
|
4348
|
-
return false;
|
|
4349
|
-
}
|
|
4350
|
-
const fieldInstance = untracked(fieldAccessor);
|
|
4351
|
-
if (!fieldInstance || typeof fieldInstance !== 'object') {
|
|
4352
|
-
warnMissingField(fieldKey, logger, warningTracker);
|
|
4353
|
-
return false;
|
|
4354
|
-
}
|
|
4355
|
-
const valueSignal = fieldInstance.value;
|
|
4356
|
-
if (isWritableSignal(valueSignal)) {
|
|
4357
|
-
valueSignal.set(value);
|
|
4358
|
-
return true;
|
|
4359
|
-
}
|
|
4360
|
-
else {
|
|
4361
|
-
warnMissingField(fieldKey, logger, warningTracker);
|
|
4362
|
-
return false;
|
|
4363
|
-
}
|
|
4395
|
+
class DfTemplate {
|
|
4396
|
+
dfTemplate = input.required(/* @ts-ignore */
|
|
4397
|
+
...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
|
|
4398
|
+
templateRef = inject(TemplateRef);
|
|
4399
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4400
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
|
|
4364
4401
|
}
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4402
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfTemplate, decorators: [{
|
|
4403
|
+
type: Directive,
|
|
4404
|
+
args: [{
|
|
4405
|
+
selector: 'ng-template[dfTemplate]',
|
|
4406
|
+
exportAs: 'dfTemplate',
|
|
4407
|
+
}]
|
|
4408
|
+
}], propDecorators: { dfTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfTemplate", required: true }] }] } });
|
|
4409
|
+
class DfFieldTemplateRegistry {
|
|
4410
|
+
_map = signal(new Map(), /* @ts-ignore */
|
|
4411
|
+
...(ngDevMode ? [{ debugName: "_map" }] : /* istanbul ignore next */ []));
|
|
4412
|
+
map = this._map.asReadonly();
|
|
4413
|
+
set(value) {
|
|
4414
|
+
this._map.set(value);
|
|
4371
4415
|
}
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
`This warning is shown once per field.`);
|
|
4416
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4417
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry });
|
|
4375
4418
|
}
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
chainContext.changedFields = changedFields;
|
|
4380
|
-
chainContext.formFieldState = createFormFieldStateMap(context.rootForm, false);
|
|
4381
|
-
const { derivationLogger } = context;
|
|
4382
|
-
const maxIterations = context.maxIterations ?? MAX_DERIVATION_ITERATIONS;
|
|
4383
|
-
let appliedCount = 0;
|
|
4384
|
-
let skippedCount = 0;
|
|
4385
|
-
let errorCount = 0;
|
|
4386
|
-
let warnCount = 0;
|
|
4387
|
-
const entriesToProcess = changedFields ? getEntriesForChangedFields$1(collection.entries, changedFields) : collection.entries;
|
|
4388
|
-
derivationLogger.cycleStart('onChange', entriesToProcess.length);
|
|
4389
|
-
let hasChanges = true;
|
|
4390
|
-
while (hasChanges && chainContext.iteration < maxIterations) {
|
|
4391
|
-
chainContext.iteration++;
|
|
4392
|
-
hasChanges = false;
|
|
4393
|
-
derivationLogger.iteration(chainContext.iteration);
|
|
4394
|
-
for (const entry of entriesToProcess) {
|
|
4395
|
-
const result = tryApplyDerivation(entry, context, chainContext);
|
|
4396
|
-
if (result.applied) {
|
|
4397
|
-
appliedCount++;
|
|
4398
|
-
hasChanges = true;
|
|
4399
|
-
}
|
|
4400
|
-
else if (result.error) {
|
|
4401
|
-
errorCount++;
|
|
4402
|
-
}
|
|
4403
|
-
else if (result.warning) {
|
|
4404
|
-
warnCount++;
|
|
4405
|
-
skippedCount++;
|
|
4406
|
-
}
|
|
4407
|
-
else {
|
|
4408
|
-
skippedCount++;
|
|
4409
|
-
}
|
|
4410
|
-
}
|
|
4411
|
-
}
|
|
4412
|
-
const processingResult = {
|
|
4413
|
-
appliedCount,
|
|
4414
|
-
skippedCount,
|
|
4415
|
-
errorCount,
|
|
4416
|
-
warnCount,
|
|
4417
|
-
iterations: chainContext.iteration,
|
|
4418
|
-
maxIterationsReached: chainContext.iteration >= maxIterations,
|
|
4419
|
-
};
|
|
4420
|
-
if (processingResult.maxIterationsReached) {
|
|
4421
|
-
derivationLogger.maxIterationsReached(processingResult, 'onChange');
|
|
4422
|
-
}
|
|
4423
|
-
derivationLogger.summary(processingResult, 'onChange');
|
|
4424
|
-
return processingResult;
|
|
4425
|
-
}
|
|
4426
|
-
function getEntriesForChangedFields$1(entries, changedFields) {
|
|
4427
|
-
return entries.filter((entry) => {
|
|
4428
|
-
if (entry.dependsOn.length === 0 || entry.dependsOn.includes('*')) {
|
|
4429
|
-
return true;
|
|
4430
|
-
}
|
|
4431
|
-
if (entry.dependsOn.some((dep) => changedFields.has(dep))) {
|
|
4432
|
-
return true;
|
|
4433
|
-
}
|
|
4434
|
-
for (const changed of changedFields) {
|
|
4435
|
-
if (entry.fieldKey.startsWith(changed + '.'))
|
|
4436
|
-
return true;
|
|
4437
|
-
if (entry.dependsOn.some((dep) => dep.startsWith(changed + '.')))
|
|
4438
|
-
return true;
|
|
4439
|
-
}
|
|
4440
|
-
return false;
|
|
4441
|
-
});
|
|
4442
|
-
}
|
|
4443
|
-
function tryApplyDerivation(entry, context, chainContext) {
|
|
4444
|
-
const { derivationLogger } = context;
|
|
4445
|
-
if (entry.http || entry.asyncFunctionName || entry.asyncFn) {
|
|
4446
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4447
|
-
}
|
|
4448
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
4449
|
-
return tryApplyArrayDerivation(entry, context, chainContext);
|
|
4450
|
-
}
|
|
4451
|
-
const derivationKey = createDerivationKey(entry.fieldKey);
|
|
4452
|
-
if (chainContext.appliedDerivations.has(derivationKey)) {
|
|
4453
|
-
derivationLogger.evaluation({
|
|
4454
|
-
debugName: entry.debugName,
|
|
4455
|
-
fieldKey: entry.fieldKey,
|
|
4456
|
-
result: 'skipped',
|
|
4457
|
-
skipReason: 'already-applied',
|
|
4458
|
-
});
|
|
4459
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4460
|
-
}
|
|
4461
|
-
if (shouldSkipForUserOverride(entry, entry.fieldKey, context, chainContext)) {
|
|
4462
|
-
derivationLogger.evaluation({
|
|
4463
|
-
debugName: entry.debugName,
|
|
4464
|
-
fieldKey: entry.fieldKey,
|
|
4465
|
-
result: 'skipped',
|
|
4466
|
-
skipReason: 'user-override',
|
|
4467
|
-
});
|
|
4468
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4469
|
-
}
|
|
4470
|
-
const formValue = untracked(() => context.formValue());
|
|
4471
|
-
const evalContext = createEvaluationContext$1(entry, formValue, context, chainContext);
|
|
4472
|
-
if (!evaluateDerivationCondition(entry.condition, evalContext)) {
|
|
4473
|
-
derivationLogger.evaluation({
|
|
4474
|
-
debugName: entry.debugName,
|
|
4475
|
-
fieldKey: entry.fieldKey,
|
|
4476
|
-
result: 'skipped',
|
|
4477
|
-
skipReason: 'condition-false',
|
|
4478
|
-
});
|
|
4479
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4480
|
-
}
|
|
4481
|
-
let newValue;
|
|
4482
|
-
try {
|
|
4483
|
-
newValue = computeDerivedValue(entry, evalContext, context);
|
|
4484
|
-
}
|
|
4485
|
-
catch (error) {
|
|
4486
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4487
|
-
context.logger.error(formatDerivationError(entry, 'compute', errorMessage));
|
|
4488
|
-
derivationLogger.evaluation({
|
|
4489
|
-
debugName: entry.debugName,
|
|
4490
|
-
fieldKey: entry.fieldKey,
|
|
4491
|
-
result: 'error',
|
|
4492
|
-
error: errorMessage,
|
|
4493
|
-
});
|
|
4494
|
-
return {
|
|
4495
|
-
applied: false,
|
|
4496
|
-
fieldKey: entry.fieldKey,
|
|
4497
|
-
error: errorMessage,
|
|
4498
|
-
};
|
|
4499
|
-
}
|
|
4500
|
-
const currentValue = getNestedValue(formValue, entry.fieldKey);
|
|
4501
|
-
if (isEqual(currentValue, newValue)) {
|
|
4502
|
-
derivationLogger.evaluation({
|
|
4503
|
-
debugName: entry.debugName,
|
|
4504
|
-
fieldKey: entry.fieldKey,
|
|
4505
|
-
result: 'skipped',
|
|
4506
|
-
skipReason: 'value-unchanged',
|
|
4507
|
-
});
|
|
4508
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4509
|
-
}
|
|
4510
|
-
try {
|
|
4511
|
-
const wasApplied = applyValueToForm(entry.fieldKey, newValue, context.rootForm, context.logger, context.warningTracker);
|
|
4512
|
-
if (wasApplied) {
|
|
4513
|
-
chainContext.appliedDerivations.add(derivationKey);
|
|
4514
|
-
derivationLogger.evaluation({
|
|
4515
|
-
debugName: entry.debugName,
|
|
4516
|
-
fieldKey: entry.fieldKey,
|
|
4517
|
-
result: 'applied',
|
|
4518
|
-
previousValue: currentValue,
|
|
4519
|
-
newValue,
|
|
4520
|
-
});
|
|
4521
|
-
return { applied: true, fieldKey: entry.fieldKey, newValue };
|
|
4522
|
-
}
|
|
4523
|
-
else {
|
|
4524
|
-
derivationLogger.evaluation({
|
|
4525
|
-
debugName: entry.debugName,
|
|
4526
|
-
fieldKey: entry.fieldKey,
|
|
4527
|
-
result: 'skipped',
|
|
4528
|
-
skipReason: 'target-not-found',
|
|
4529
|
-
});
|
|
4530
|
-
return {
|
|
4531
|
-
applied: false,
|
|
4532
|
-
fieldKey: entry.fieldKey,
|
|
4533
|
-
warning: `Field '${entry.fieldKey}' not found`,
|
|
4534
|
-
};
|
|
4535
|
-
}
|
|
4536
|
-
}
|
|
4537
|
-
catch (error) {
|
|
4538
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4539
|
-
context.logger.error(formatDerivationError(entry, 'apply', errorMessage));
|
|
4540
|
-
derivationLogger.evaluation({
|
|
4541
|
-
debugName: entry.debugName,
|
|
4542
|
-
fieldKey: entry.fieldKey,
|
|
4543
|
-
result: 'error',
|
|
4544
|
-
error: errorMessage,
|
|
4545
|
-
});
|
|
4546
|
-
return {
|
|
4547
|
-
applied: false,
|
|
4548
|
-
fieldKey: entry.fieldKey,
|
|
4549
|
-
error: errorMessage,
|
|
4550
|
-
};
|
|
4551
|
-
}
|
|
4552
|
-
}
|
|
4553
|
-
function tryApplyArrayDerivation(entry, context, chainContext) {
|
|
4554
|
-
const formValue = untracked(() => context.formValue());
|
|
4555
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
4556
|
-
if (!pathInfo.isArrayPath) {
|
|
4557
|
-
return { applied: false, fieldKey: entry.fieldKey, error: 'Invalid array derivation path' };
|
|
4558
|
-
}
|
|
4559
|
-
const { arrayPath } = pathInfo;
|
|
4560
|
-
const arrayValue = getNestedValue(formValue, arrayPath);
|
|
4561
|
-
if (!Array.isArray(arrayValue)) {
|
|
4562
|
-
return { applied: false, fieldKey: entry.fieldKey };
|
|
4563
|
-
}
|
|
4564
|
-
let appliedAny = false;
|
|
4565
|
-
for (let i = 0; i < arrayValue.length; i++) {
|
|
4566
|
-
const resolvedPath = resolveArrayPath(entry.fieldKey, i);
|
|
4567
|
-
const derivationKey = createDerivationKey(resolvedPath);
|
|
4568
|
-
if (chainContext.appliedDerivations.has(derivationKey)) {
|
|
4569
|
-
continue;
|
|
4570
|
-
}
|
|
4571
|
-
if (shouldSkipForUserOverride(entry, resolvedPath, context, chainContext)) {
|
|
4572
|
-
context.derivationLogger.evaluation({
|
|
4573
|
-
debugName: entry.debugName,
|
|
4574
|
-
fieldKey: resolvedPath,
|
|
4575
|
-
result: 'skipped',
|
|
4576
|
-
skipReason: 'user-override',
|
|
4577
|
-
});
|
|
4578
|
-
continue;
|
|
4579
|
-
}
|
|
4580
|
-
const arrayItem = arrayValue[i];
|
|
4581
|
-
const evalContext = createArrayItemEvaluationContext$1(entry, arrayItem, formValue, i, arrayPath, context, chainContext);
|
|
4582
|
-
if (!evaluateDerivationCondition(entry.condition, evalContext)) {
|
|
4583
|
-
continue;
|
|
4584
|
-
}
|
|
4585
|
-
let newValue;
|
|
4586
|
-
try {
|
|
4587
|
-
newValue = computeDerivedValue(entry, evalContext, context);
|
|
4588
|
-
}
|
|
4589
|
-
catch (error) {
|
|
4590
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4591
|
-
context.logger.error(formatDerivationError(entry, 'compute', `[index ${i}] ${errorMessage}`));
|
|
4592
|
-
continue;
|
|
4593
|
-
}
|
|
4594
|
-
const currentValue = getNestedValue(formValue, resolvedPath);
|
|
4595
|
-
if (isEqual(currentValue, newValue)) {
|
|
4596
|
-
continue;
|
|
4597
|
-
}
|
|
4598
|
-
try {
|
|
4599
|
-
applyValueToForm(resolvedPath, newValue, context.rootForm, context.logger, context.warningTracker);
|
|
4600
|
-
chainContext.appliedDerivations.add(derivationKey);
|
|
4601
|
-
appliedAny = true;
|
|
4602
|
-
}
|
|
4603
|
-
catch (error) {
|
|
4604
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4605
|
-
context.logger.error(formatDerivationError(entry, 'apply', `[index ${i}] ${errorMessage}`));
|
|
4606
|
-
}
|
|
4607
|
-
}
|
|
4608
|
-
return { applied: appliedAny, fieldKey: entry.fieldKey };
|
|
4609
|
-
}
|
|
4610
|
-
function createEvaluationContext$1(entry, formValue, context, chainContext) {
|
|
4611
|
-
const fieldValue = getNestedValue(formValue, entry.fieldKey);
|
|
4612
|
-
const fieldAccessor = context.rootForm[entry.fieldKey];
|
|
4613
|
-
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
4614
|
-
const parentPath = getParentPathInScope(entry.fieldKey);
|
|
4615
|
-
const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
|
|
4616
|
-
return {
|
|
4617
|
-
fieldValue,
|
|
4618
|
-
formValue,
|
|
4619
|
-
fieldPath: entry.fieldKey,
|
|
4620
|
-
groupValue,
|
|
4621
|
-
customFunctions: context.customFunctions,
|
|
4622
|
-
externalData: context.externalData,
|
|
4623
|
-
logger: context.logger,
|
|
4624
|
-
fieldState,
|
|
4625
|
-
formFieldState: chainContext.formFieldState,
|
|
4626
|
-
};
|
|
4627
|
-
}
|
|
4628
|
-
function createArrayItemEvaluationContext$1(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context, chainContext) {
|
|
4629
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
4630
|
-
let fieldState;
|
|
4631
|
-
if (pathInfo.isArrayPath && pathInfo.relativePath) {
|
|
4632
|
-
const rootFormRecord = context.rootForm;
|
|
4633
|
-
const arrayAccessor = rootFormRecord[arrayPath];
|
|
4634
|
-
if (arrayAccessor) {
|
|
4635
|
-
const arrayItems = arrayAccessor;
|
|
4636
|
-
const itemAccessor = arrayItems[String(itemIndex)];
|
|
4637
|
-
if (itemAccessor) {
|
|
4638
|
-
const itemFields = itemAccessor;
|
|
4639
|
-
const fieldAccessor = itemFields[pathInfo.relativePath];
|
|
4640
|
-
if (fieldAccessor) {
|
|
4641
|
-
fieldState = readFieldStateInfo(fieldAccessor, false);
|
|
4642
|
-
}
|
|
4643
|
-
}
|
|
4644
|
-
}
|
|
4645
|
-
}
|
|
4646
|
-
const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
|
|
4647
|
-
return {
|
|
4648
|
-
fieldValue,
|
|
4649
|
-
formValue: arrayItem,
|
|
4650
|
-
fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
|
|
4651
|
-
groupValue,
|
|
4652
|
-
customFunctions: context.customFunctions,
|
|
4653
|
-
externalData: context.externalData,
|
|
4654
|
-
logger: context.logger,
|
|
4655
|
-
rootFormValue,
|
|
4656
|
-
arrayIndex: itemIndex,
|
|
4657
|
-
arrayPath,
|
|
4658
|
-
fieldState,
|
|
4659
|
-
formFieldState: chainContext.formFieldState,
|
|
4660
|
-
};
|
|
4661
|
-
}
|
|
4662
|
-
function evaluateDerivationCondition(condition, context) {
|
|
4663
|
-
if (typeof condition === 'boolean') {
|
|
4664
|
-
return condition;
|
|
4665
|
-
}
|
|
4666
|
-
return evaluateCondition(condition, context);
|
|
4667
|
-
}
|
|
4668
|
-
function computeDerivedValue(entry, evalContext, applicatorContext) {
|
|
4669
|
-
return computeValueFromEntry(entry, evalContext, {
|
|
4670
|
-
derivationFunctions: applicatorContext.derivationFunctions,
|
|
4671
|
-
subject: entry.fieldKey,
|
|
4672
|
-
});
|
|
4673
|
-
}
|
|
4674
|
-
const ERROR_PREFIX$1 = '[Derivation]';
|
|
4675
|
-
function formatDerivationError(entry, phase, message) {
|
|
4676
|
-
const source = entry.expression
|
|
4677
|
-
? `expression: "${entry.expression.substring(0, 50)}${entry.expression.length > 50 ? '...' : ''}"`
|
|
4678
|
-
: entry.functionName
|
|
4679
|
-
? `function: "${entry.functionName}"`
|
|
4680
|
-
: entry.fn
|
|
4681
|
-
? 'inline fn'
|
|
4682
|
-
: entry.value !== undefined
|
|
4683
|
-
? 'static value'
|
|
4684
|
-
: 'unknown source';
|
|
4685
|
-
return `${ERROR_PREFIX$1} Failed to ${phase} derivation (field: ${entry.fieldKey}, ${source}): ${message}`;
|
|
4686
|
-
}
|
|
4687
|
-
function hasDependencyChanged(entry, changedFields) {
|
|
4688
|
-
return entry.dependsOn.some((dep) => dep === '*' || changedFields.has(dep));
|
|
4689
|
-
}
|
|
4690
|
-
function shouldSkipForUserOverride(entry, resolvedFieldKey, context, chainContext) {
|
|
4691
|
-
if (!entry.stopOnUserOverride)
|
|
4692
|
-
return false;
|
|
4693
|
-
const isDirty = readFieldDirty(context.rootForm, resolvedFieldKey) ?? false;
|
|
4694
|
-
if (!isDirty)
|
|
4695
|
-
return false;
|
|
4696
|
-
if (entry.reEngageOnDependencyChange && chainContext.changedFields) {
|
|
4697
|
-
if (hasDependencyChanged(entry, chainContext.changedFields)) {
|
|
4698
|
-
resetFieldState(context.rootForm, resolvedFieldKey);
|
|
4699
|
-
return false;
|
|
4700
|
-
}
|
|
4701
|
-
}
|
|
4702
|
-
return true;
|
|
4703
|
-
}
|
|
4704
|
-
function applyDerivationsForTrigger(collection, trigger, context, changedFields) {
|
|
4705
|
-
const filteredEntries = collection.entries.filter((entry) => {
|
|
4706
|
-
if (entry.http || entry.asyncFunctionName || entry.asyncFn)
|
|
4707
|
-
return false;
|
|
4708
|
-
if (trigger === 'onChange') {
|
|
4709
|
-
return !entry.trigger || entry.trigger === 'onChange';
|
|
4710
|
-
}
|
|
4711
|
-
return entry.trigger === trigger;
|
|
4712
|
-
});
|
|
4713
|
-
const filteredCollection = {
|
|
4714
|
-
entries: filteredEntries,
|
|
4715
|
-
};
|
|
4716
|
-
return applyDerivations(filteredCollection, context, changedFields);
|
|
4717
|
-
}
|
|
4718
|
-
function getDebouncedDerivationEntries(collection) {
|
|
4719
|
-
return collection.entries.filter((entry) => entry.trigger === 'debounced');
|
|
4720
|
-
}
|
|
4721
|
-
|
|
4722
|
-
function topologicalSort(entries) {
|
|
4723
|
-
if (entries.length <= 1) {
|
|
4724
|
-
return [...entries];
|
|
4725
|
-
}
|
|
4726
|
-
const graph = new Map();
|
|
4727
|
-
const inDegree = new Map();
|
|
4728
|
-
for (const entry of entries) {
|
|
4729
|
-
graph.set(entry, new Set());
|
|
4730
|
-
inDegree.set(entry, 0);
|
|
4731
|
-
}
|
|
4732
|
-
const derivationsByField = new Map();
|
|
4733
|
-
for (const entry of entries) {
|
|
4734
|
-
const fieldEntries = derivationsByField.get(entry.fieldKey) ?? [];
|
|
4735
|
-
fieldEntries.push(entry);
|
|
4736
|
-
derivationsByField.set(entry.fieldKey, fieldEntries);
|
|
4737
|
-
if (entry.fieldKey.includes('.$.')) {
|
|
4738
|
-
const relativePath = entry.fieldKey.split('.$.')[1];
|
|
4739
|
-
if (relativePath) {
|
|
4740
|
-
const relativeEntries = derivationsByField.get(relativePath) ?? [];
|
|
4741
|
-
relativeEntries.push(entry);
|
|
4742
|
-
derivationsByField.set(relativePath, relativeEntries);
|
|
4743
|
-
}
|
|
4744
|
-
}
|
|
4745
|
-
}
|
|
4746
|
-
const wildcardDependents = [];
|
|
4747
|
-
for (const entry of entries) {
|
|
4748
|
-
if (entry.dependsOn.includes('*')) {
|
|
4749
|
-
wildcardDependents.push(entry);
|
|
4750
|
-
}
|
|
4751
|
-
}
|
|
4752
|
-
for (const entryB of entries) {
|
|
4753
|
-
for (const dep of entryB.dependsOn) {
|
|
4754
|
-
if (dep === '*')
|
|
4755
|
-
continue;
|
|
4756
|
-
const producers = derivationsByField.get(dep);
|
|
4757
|
-
if (producers) {
|
|
4758
|
-
for (const entryA of producers) {
|
|
4759
|
-
const entryAEdges = graph.get(entryA);
|
|
4760
|
-
if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
|
|
4761
|
-
entryAEdges.add(entryB);
|
|
4762
|
-
inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
|
|
4763
|
-
}
|
|
4764
|
-
}
|
|
4765
|
-
}
|
|
4766
|
-
}
|
|
4767
|
-
}
|
|
4768
|
-
for (const entryB of wildcardDependents) {
|
|
4769
|
-
for (const entryA of entries) {
|
|
4770
|
-
const entryAEdges = graph.get(entryA);
|
|
4771
|
-
if (entryA !== entryB && entryAEdges && !entryAEdges.has(entryB)) {
|
|
4772
|
-
entryAEdges.add(entryB);
|
|
4773
|
-
inDegree.set(entryB, (inDegree.get(entryB) ?? 0) + 1);
|
|
4774
|
-
}
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
const queue = [];
|
|
4778
|
-
const sorted = [];
|
|
4779
|
-
for (const entry of entries) {
|
|
4780
|
-
if (inDegree.get(entry) === 0) {
|
|
4781
|
-
queue.push(entry);
|
|
4782
|
-
}
|
|
4783
|
-
}
|
|
4784
|
-
while (queue.length > 0) {
|
|
4785
|
-
const current = queue.shift();
|
|
4786
|
-
if (!current)
|
|
4787
|
-
break; sorted.push(current);
|
|
4788
|
-
const neighbors = graph.get(current) ?? new Set();
|
|
4789
|
-
for (const neighbor of neighbors) {
|
|
4790
|
-
const newDegree = (inDegree.get(neighbor) ?? 0) - 1;
|
|
4791
|
-
inDegree.set(neighbor, newDegree);
|
|
4792
|
-
if (newDegree === 0) {
|
|
4793
|
-
queue.push(neighbor);
|
|
4794
|
-
}
|
|
4795
|
-
}
|
|
4796
|
-
}
|
|
4797
|
-
if (sorted.length !== entries.length) {
|
|
4798
|
-
const sortedSet = new Set(sorted);
|
|
4799
|
-
for (const entry of entries) {
|
|
4800
|
-
if (!sortedSet.has(entry)) {
|
|
4801
|
-
sorted.push(entry);
|
|
4802
|
-
}
|
|
4803
|
-
}
|
|
4804
|
-
}
|
|
4805
|
-
return sorted;
|
|
4806
|
-
}
|
|
4807
|
-
|
|
4808
|
-
function extractDependenciesFromConfig(config) {
|
|
4809
|
-
const deps = new Set();
|
|
4810
|
-
if (config.dependsOn && config.dependsOn.length > 0) {
|
|
4811
|
-
config.dependsOn.forEach((dep) => deps.add(dep));
|
|
4812
|
-
}
|
|
4813
|
-
else {
|
|
4814
|
-
if (config.expression) {
|
|
4815
|
-
extractStringDependencies(config.expression).forEach((dep) => deps.add(dep));
|
|
4816
|
-
}
|
|
4817
|
-
if (config.functionName || config.fn) {
|
|
4818
|
-
deps.add('*');
|
|
4819
|
-
}
|
|
4820
|
-
}
|
|
4821
|
-
if (config.condition && config.condition !== true) {
|
|
4822
|
-
extractExpressionDependencies(config.condition).forEach((dep) => deps.add(dep));
|
|
4823
|
-
}
|
|
4824
|
-
return Array.from(deps);
|
|
4825
|
-
}
|
|
4826
|
-
|
|
4827
|
-
function extractInlineFn(config) {
|
|
4828
|
-
return config.fn;
|
|
4829
|
-
}
|
|
4830
|
-
function extractInlineAsyncFn(config) {
|
|
4831
|
-
return config.asyncFn;
|
|
4832
|
-
}
|
|
4833
|
-
|
|
4834
|
-
function traverseFieldsWithContext(fields, context, visitor, hooks) {
|
|
4835
|
-
for (const field of fields) {
|
|
4836
|
-
visitor(field, context);
|
|
4837
|
-
if (!hasChildFields(field))
|
|
4838
|
-
continue;
|
|
4839
|
-
if (field.type === 'array') {
|
|
4840
|
-
const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
|
|
4841
|
-
const arrayChildren = collectArrayChildren(field);
|
|
4842
|
-
traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
|
|
4843
|
-
continue;
|
|
4844
|
-
}
|
|
4845
|
-
const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
|
|
4846
|
-
const childContext = mergeContext(context, overrides);
|
|
4847
|
-
traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
|
|
4848
|
-
}
|
|
4849
|
-
}
|
|
4850
|
-
function collectArrayChildren(field) {
|
|
4851
|
-
let arrayItems = normalizeFieldsArray(field.fields);
|
|
4852
|
-
if (arrayItems.length === 0) {
|
|
4853
|
-
const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
|
|
4854
|
-
if (metadataTemplate) {
|
|
4855
|
-
arrayItems = [
|
|
4856
|
-
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
4857
|
-
];
|
|
4858
|
-
}
|
|
4859
|
-
}
|
|
4860
|
-
const flattened = [];
|
|
4861
|
-
for (const item of arrayItems) {
|
|
4862
|
-
if (Array.isArray(item)) {
|
|
4863
|
-
flattened.push(...item);
|
|
4864
|
-
}
|
|
4865
|
-
else {
|
|
4866
|
-
flattened.push(item);
|
|
4867
|
-
}
|
|
4868
|
-
}
|
|
4869
|
-
return flattened;
|
|
4870
|
-
}
|
|
4871
|
-
function mergeContext(base, overrides) {
|
|
4872
|
-
return overrides ? { ...base, ...overrides } : { ...base };
|
|
4873
|
-
}
|
|
4874
|
-
|
|
4875
|
-
const SELF_DEPENDENCY_TOKEN = '$self';
|
|
4876
|
-
const GROUP_DEPENDENCY_TOKEN = '$group';
|
|
4877
|
-
function buildEffectiveFieldKey(fieldKey, context) {
|
|
4878
|
-
if (context.arrayPath && context.groupPath) {
|
|
4879
|
-
return `${context.arrayPath}.$.${context.groupPath}.${fieldKey}`;
|
|
4880
|
-
}
|
|
4881
|
-
if (context.arrayPath) {
|
|
4882
|
-
return `${context.arrayPath}.$.${fieldKey}`;
|
|
4883
|
-
}
|
|
4884
|
-
if (context.groupPath) {
|
|
4885
|
-
return `${context.groupPath}.${fieldKey}`;
|
|
4886
|
-
}
|
|
4887
|
-
return fieldKey;
|
|
4888
|
-
}
|
|
4889
|
-
function buildNearestGroupPath(context) {
|
|
4890
|
-
if (context.arrayPath && context.groupPath) {
|
|
4891
|
-
return `${context.arrayPath}.$.${context.groupPath}`;
|
|
4892
|
-
}
|
|
4893
|
-
return context.arrayPath ?? context.groupPath;
|
|
4894
|
-
}
|
|
4895
|
-
function resolveTokenDependencies(deps, effectiveFieldKey, context) {
|
|
4896
|
-
return deps.map((dep) => {
|
|
4897
|
-
if (dep === SELF_DEPENDENCY_TOKEN) {
|
|
4898
|
-
return effectiveFieldKey;
|
|
4899
|
-
}
|
|
4900
|
-
if (dep.startsWith(`${SELF_DEPENDENCY_TOKEN}.`)) {
|
|
4901
|
-
return effectiveFieldKey + dep.slice(SELF_DEPENDENCY_TOKEN.length);
|
|
4902
|
-
}
|
|
4903
|
-
if (dep === GROUP_DEPENDENCY_TOKEN || dep.startsWith(`${GROUP_DEPENDENCY_TOKEN}.`)) {
|
|
4904
|
-
const groupPath = buildNearestGroupPath(context);
|
|
4905
|
-
if (!groupPath) {
|
|
4906
|
-
throw new DynamicFormError(`Derivation for '${effectiveFieldKey}' uses '${dep}' but the field has no parent group or array. ` +
|
|
4907
|
-
`'${GROUP_DEPENDENCY_TOKEN}' is only valid for fields nested inside a group or array container.`);
|
|
4908
|
-
}
|
|
4909
|
-
if (dep === GROUP_DEPENDENCY_TOKEN)
|
|
4910
|
-
return groupPath;
|
|
4911
|
-
return groupPath + dep.slice(GROUP_DEPENDENCY_TOKEN.length);
|
|
4912
|
-
}
|
|
4913
|
-
return dep;
|
|
4914
|
-
});
|
|
4915
|
-
}
|
|
4916
|
-
const CONTEXT_TRAVERSAL_OPTIONS = {
|
|
4917
|
-
onArrayChild: (_parent, field) => ({ arrayPath: field.key, groupPath: undefined }),
|
|
4918
|
-
onGroupChild: (parent, field) => {
|
|
4919
|
-
if (!field.key)
|
|
4920
|
-
return {};
|
|
4921
|
-
return { groupPath: parent.groupPath ? `${parent.groupPath}.${field.key}` : field.key };
|
|
4922
|
-
},
|
|
4923
|
-
};
|
|
4924
|
-
|
|
4925
|
-
function collectDerivations(fields) {
|
|
4926
|
-
const entries = [];
|
|
4927
|
-
traverseFieldsWithContext(fields, {}, (field, context) => collectValueEntriesFromField(field, entries, context), CONTEXT_TRAVERSAL_OPTIONS);
|
|
4928
|
-
return { entries: topologicalSort(entries) };
|
|
4929
|
-
}
|
|
4930
|
-
function collectAllDerivations(fields) {
|
|
4931
|
-
const valueEntries = [];
|
|
4932
|
-
const propertyEntries = [];
|
|
4933
|
-
traverseFieldsWithContext(fields, {}, (field, context) => {
|
|
4934
|
-
collectValueEntriesFromField(field, valueEntries, context);
|
|
4935
|
-
collectPropertyEntriesFromField(field, propertyEntries, context);
|
|
4936
|
-
}, CONTEXT_TRAVERSAL_OPTIONS);
|
|
4937
|
-
return {
|
|
4938
|
-
value: { entries: topologicalSort(valueEntries) },
|
|
4939
|
-
property: { entries: propertyEntries },
|
|
4940
|
-
};
|
|
4941
|
-
}
|
|
4942
|
-
function collectValueEntriesFromField(field, entries, context) {
|
|
4943
|
-
const fieldKey = field.key;
|
|
4944
|
-
if (!fieldKey)
|
|
4945
|
-
return;
|
|
4946
|
-
const validationField = field;
|
|
4947
|
-
if (validationField.derivation) {
|
|
4948
|
-
entries.push(createShorthandEntry(fieldKey, validationField.derivation, context));
|
|
4949
|
-
}
|
|
4950
|
-
if (validationField.logic) {
|
|
4951
|
-
for (const logicConfig of validationField.logic) {
|
|
4952
|
-
if (!isDerivationLogicConfig(logicConfig))
|
|
4953
|
-
continue;
|
|
4954
|
-
if (hasTargetProperty(logicConfig))
|
|
4955
|
-
continue; entries.push(createValueLogicEntry(fieldKey, logicConfig, context));
|
|
4956
|
-
}
|
|
4957
|
-
}
|
|
4958
|
-
}
|
|
4959
|
-
function collectPropertyEntriesFromField(field, entries, context) {
|
|
4960
|
-
const fieldKey = field.key;
|
|
4961
|
-
if (!fieldKey)
|
|
4962
|
-
return;
|
|
4963
|
-
const validationField = field;
|
|
4964
|
-
if (!validationField.logic)
|
|
4965
|
-
return;
|
|
4966
|
-
for (const logicConfig of validationField.logic) {
|
|
4967
|
-
if (!isDerivationLogicConfig(logicConfig))
|
|
4968
|
-
continue;
|
|
4969
|
-
if (!hasTargetProperty(logicConfig))
|
|
4970
|
-
continue;
|
|
4971
|
-
entries.push(createPropertyLogicEntry(fieldKey, logicConfig, context));
|
|
4972
|
-
}
|
|
4973
|
-
}
|
|
4974
|
-
function createShorthandEntry(fieldKey, expression, context) {
|
|
4975
|
-
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
4976
|
-
return {
|
|
4977
|
-
fieldKey: effectiveFieldKey,
|
|
4978
|
-
dependsOn: extractStringDependencies(expression),
|
|
4979
|
-
condition: true,
|
|
4980
|
-
expression,
|
|
4981
|
-
trigger: 'onChange',
|
|
4982
|
-
isShorthand: true,
|
|
4983
|
-
};
|
|
4984
|
-
}
|
|
4985
|
-
function createValueLogicEntry(fieldKey, config, context) {
|
|
4986
|
-
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
4987
|
-
if (config.source === 'http') {
|
|
4988
|
-
if (config.dependsOn.length === 0) {
|
|
4989
|
-
throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
|
|
4990
|
-
`Wildcard dependencies would trigger HTTP requests on every form change.`);
|
|
4991
|
-
}
|
|
4992
|
-
if (config.dependsOn.includes('*')) {
|
|
4993
|
-
throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
4994
|
-
`Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
|
|
4995
|
-
}
|
|
4996
|
-
}
|
|
4997
|
-
if (config.source === 'asyncFunction') {
|
|
4998
|
-
if (config.dependsOn.length === 0) {
|
|
4999
|
-
throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
|
|
5000
|
-
`Wildcard dependencies would trigger async functions on every form change.`);
|
|
5001
|
-
}
|
|
5002
|
-
if (config.dependsOn.includes('*')) {
|
|
5003
|
-
throw new DynamicFormError(`Async derivation for '${effectiveFieldKey}' cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
5004
|
-
`Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
|
|
5005
|
-
}
|
|
5006
|
-
}
|
|
5007
|
-
const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
|
|
5008
|
-
const condition = config.condition ?? true;
|
|
5009
|
-
const trigger = config.trigger ?? 'onChange';
|
|
5010
|
-
const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
|
|
5011
|
-
return {
|
|
5012
|
-
fieldKey: effectiveFieldKey,
|
|
5013
|
-
dependsOn,
|
|
5014
|
-
condition,
|
|
5015
|
-
value: config.value,
|
|
5016
|
-
expression: config.expression,
|
|
5017
|
-
functionName: config.functionName,
|
|
5018
|
-
fn: extractInlineFn(config),
|
|
5019
|
-
http: config.http,
|
|
5020
|
-
responseExpression: config.responseExpression,
|
|
5021
|
-
asyncFunctionName: config.asyncFunctionName,
|
|
5022
|
-
asyncFn: extractInlineAsyncFn(config),
|
|
5023
|
-
trigger,
|
|
5024
|
-
debounceMs,
|
|
5025
|
-
isShorthand: false,
|
|
5026
|
-
originalConfig: config,
|
|
5027
|
-
debugName: config.debugName,
|
|
5028
|
-
stopOnUserOverride: config.stopOnUserOverride,
|
|
5029
|
-
reEngageOnDependencyChange: config.reEngageOnDependencyChange,
|
|
5030
|
-
};
|
|
5031
|
-
}
|
|
5032
|
-
function createPropertyLogicEntry(fieldKey, config, context) {
|
|
5033
|
-
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
5034
|
-
const errorLocation = `'${effectiveFieldKey}.${config.targetProperty}'`;
|
|
5035
|
-
const untypedConfig = config;
|
|
5036
|
-
if (untypedConfig.source === 'http' || untypedConfig.source === 'asyncFunction') {
|
|
5037
|
-
const conflictingSyncSources = [];
|
|
5038
|
-
if (untypedConfig.value !== undefined)
|
|
5039
|
-
conflictingSyncSources.push('value');
|
|
5040
|
-
if (untypedConfig.expression !== undefined)
|
|
5041
|
-
conflictingSyncSources.push('expression');
|
|
5042
|
-
if (untypedConfig.functionName !== undefined)
|
|
5043
|
-
conflictingSyncSources.push('functionName');
|
|
5044
|
-
if (untypedConfig.fn !== undefined)
|
|
5045
|
-
conflictingSyncSources.push('fn');
|
|
5046
|
-
if (conflictingSyncSources.length > 0) {
|
|
5047
|
-
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: ${JSON.stringify(untypedConfig.source)}' alongside ` +
|
|
5048
|
-
`sync value source(s) [${conflictingSyncSources.join(', ')}]. These are mutually exclusive — remove either ` +
|
|
5049
|
-
`the sync fields or the async source.`);
|
|
5050
|
-
}
|
|
5051
|
-
}
|
|
5052
|
-
if (untypedConfig.source === 'http' && (untypedConfig.asyncFunctionName !== undefined || untypedConfig.asyncFn !== undefined)) {
|
|
5053
|
-
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "http"' alongside an async function — these are mutually exclusive.`);
|
|
5054
|
-
}
|
|
5055
|
-
if (untypedConfig.source === 'asyncFunction' && (untypedConfig.http !== undefined || untypedConfig.responseExpression !== undefined)) {
|
|
5056
|
-
throw new DynamicFormError(`Property derivation for ${errorLocation} sets 'source: "asyncFunction"' alongside HTTP fields — these are mutually exclusive.`);
|
|
5057
|
-
}
|
|
5058
|
-
if (untypedConfig.source === 'http') {
|
|
5059
|
-
if (!untypedConfig.http) {
|
|
5060
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires an 'http' config object.`);
|
|
5061
|
-
}
|
|
5062
|
-
if (typeof untypedConfig.responseExpression !== 'string' || untypedConfig.responseExpression.trim() === '') {
|
|
5063
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires a non-empty 'responseExpression'.`);
|
|
5064
|
-
}
|
|
5065
|
-
if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
|
|
5066
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
|
|
5067
|
-
`Wildcard dependencies would trigger HTTP requests on every form change.`);
|
|
5068
|
-
}
|
|
5069
|
-
if (untypedConfig.dependsOn.includes('*')) {
|
|
5070
|
-
throw new DynamicFormError(`HTTP property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
5071
|
-
`Wildcards would trigger HTTP requests on every form change. Specify explicit field dependencies instead.`);
|
|
5072
|
-
}
|
|
5073
|
-
}
|
|
5074
|
-
if (untypedConfig.source === 'asyncFunction') {
|
|
5075
|
-
if (!untypedConfig.asyncFunctionName && !untypedConfig.asyncFn) {
|
|
5076
|
-
throw new DynamicFormError(`Async property derivation for ${errorLocation} requires either 'asyncFunctionName' or 'asyncFn'.`);
|
|
5077
|
-
}
|
|
5078
|
-
if (!Array.isArray(untypedConfig.dependsOn) || untypedConfig.dependsOn.length === 0) {
|
|
5079
|
-
throw new DynamicFormError(`Async property derivation for ${errorLocation} requires explicit 'dependsOn'. ` +
|
|
5080
|
-
`Wildcard dependencies would trigger async functions on every form change.`);
|
|
5081
|
-
}
|
|
5082
|
-
if (untypedConfig.dependsOn.includes('*')) {
|
|
5083
|
-
throw new DynamicFormError(`Async property derivation for ${errorLocation} cannot use wildcard ('*') in 'dependsOn'. ` +
|
|
5084
|
-
`Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
|
|
5085
|
-
}
|
|
5086
|
-
}
|
|
5087
|
-
const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
|
|
5088
|
-
const condition = config.condition ?? true;
|
|
5089
|
-
const trigger = config.trigger ?? 'onChange';
|
|
5090
|
-
const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
|
|
5091
|
-
return {
|
|
5092
|
-
fieldKey: effectiveFieldKey,
|
|
5093
|
-
targetProperty: config.targetProperty,
|
|
5094
|
-
dependsOn,
|
|
5095
|
-
condition,
|
|
5096
|
-
value: config.value,
|
|
5097
|
-
expression: config.expression,
|
|
5098
|
-
functionName: config.functionName,
|
|
5099
|
-
fn: extractInlineFn(config),
|
|
5100
|
-
http: config.http,
|
|
5101
|
-
responseExpression: config.responseExpression,
|
|
5102
|
-
asyncFunctionName: config.asyncFunctionName,
|
|
5103
|
-
asyncFn: extractInlineAsyncFn(config),
|
|
5104
|
-
trigger,
|
|
5105
|
-
debounceMs,
|
|
5106
|
-
debugName: config.debugName,
|
|
5107
|
-
originalConfig: config,
|
|
5108
|
-
};
|
|
5109
|
-
}
|
|
5110
|
-
|
|
5111
|
-
function collectPropertyDerivations(fields) {
|
|
5112
|
-
return collectAllDerivations(fields).property;
|
|
5113
|
-
}
|
|
5114
|
-
|
|
5115
|
-
function detectCycles(collection) {
|
|
5116
|
-
const graph = buildDependencyGraph(collection);
|
|
5117
|
-
if (graph.size === 0) {
|
|
5118
|
-
return { hasCycle: false };
|
|
5119
|
-
}
|
|
5120
|
-
const bidirectionalPairs = detectBidirectionalPairs(collection);
|
|
5121
|
-
const result = detectCyclesWithDFS(graph, bidirectionalPairs);
|
|
5122
|
-
if (bidirectionalPairs.size > 0) {
|
|
5123
|
-
result.bidirectionalPairs = Array.from(bidirectionalPairs);
|
|
5124
|
-
}
|
|
5125
|
-
return result;
|
|
5126
|
-
}
|
|
5127
|
-
function detectBidirectionalPairs(collection) {
|
|
5128
|
-
const pairs = new Set();
|
|
5129
|
-
const dependencyMap = new Map();
|
|
5130
|
-
for (const entry of collection.entries) {
|
|
5131
|
-
const deps = dependencyMap.get(entry.fieldKey) ?? new Set();
|
|
5132
|
-
for (const dep of entry.dependsOn) {
|
|
5133
|
-
if (dep !== '*' && dep !== entry.fieldKey) {
|
|
5134
|
-
deps.add(dep);
|
|
5135
|
-
}
|
|
5136
|
-
}
|
|
5137
|
-
dependencyMap.set(entry.fieldKey, deps);
|
|
5138
|
-
}
|
|
5139
|
-
for (const [fieldA, depsA] of dependencyMap) {
|
|
5140
|
-
for (const fieldB of depsA) {
|
|
5141
|
-
const depsB = dependencyMap.get(fieldB);
|
|
5142
|
-
if (depsB?.has(fieldA)) {
|
|
5143
|
-
const pairKey = fieldA < fieldB ? `${fieldA}↔${fieldB}` : `${fieldB}↔${fieldA}`;
|
|
5144
|
-
pairs.add(pairKey);
|
|
5145
|
-
}
|
|
5146
|
-
}
|
|
5147
|
-
}
|
|
5148
|
-
return pairs;
|
|
5149
|
-
}
|
|
5150
|
-
function isBidirectionalCycle(cyclePath, bidirectionalPairs) {
|
|
5151
|
-
if (cyclePath.length !== 3) {
|
|
5152
|
-
return false;
|
|
5153
|
-
}
|
|
5154
|
-
const [first, second, third] = cyclePath;
|
|
5155
|
-
if (first !== third) {
|
|
5156
|
-
return false;
|
|
5157
|
-
}
|
|
5158
|
-
const pairKey = first < second ? `${first}↔${second}` : `${second}↔${first}`;
|
|
5159
|
-
return bidirectionalPairs.has(pairKey);
|
|
5160
|
-
}
|
|
5161
|
-
function buildDependencyGraph(collection) {
|
|
5162
|
-
const graph = new Map();
|
|
5163
|
-
const ensureNode = (fieldKey) => {
|
|
5164
|
-
let node = graph.get(fieldKey);
|
|
5165
|
-
if (!node) {
|
|
5166
|
-
node = {
|
|
5167
|
-
fieldKey,
|
|
5168
|
-
dependsOn: new Set(),
|
|
5169
|
-
dependedOnBy: new Set(),
|
|
5170
|
-
};
|
|
5171
|
-
graph.set(fieldKey, node);
|
|
5172
|
-
}
|
|
5173
|
-
return node;
|
|
5174
|
-
};
|
|
5175
|
-
for (const entry of collection.entries) {
|
|
5176
|
-
const fieldNode = ensureNode(entry.fieldKey);
|
|
5177
|
-
for (const dep of entry.dependsOn) {
|
|
5178
|
-
if (dep !== '*' && dep !== entry.fieldKey) {
|
|
5179
|
-
const depNode = ensureNode(dep);
|
|
5180
|
-
fieldNode.dependsOn.add(dep);
|
|
5181
|
-
depNode.dependedOnBy.add(entry.fieldKey);
|
|
5182
|
-
}
|
|
5183
|
-
}
|
|
5184
|
-
}
|
|
5185
|
-
return graph;
|
|
5186
|
-
}
|
|
5187
|
-
function detectCyclesWithDFS(graph, bidirectionalPairs) {
|
|
5188
|
-
const visitState = new Map();
|
|
5189
|
-
const parent = new Map();
|
|
5190
|
-
for (const fieldKey of graph.keys()) {
|
|
5191
|
-
visitState.set(fieldKey, 0 /* VisitState.Unvisited */);
|
|
5192
|
-
}
|
|
5193
|
-
for (const fieldKey of graph.keys()) {
|
|
5194
|
-
if (visitState.get(fieldKey) === 0 /* VisitState.Unvisited */) {
|
|
5195
|
-
const cycleResult = dfsVisit(fieldKey, graph, visitState, parent, [], bidirectionalPairs);
|
|
5196
|
-
if (cycleResult) {
|
|
5197
|
-
return cycleResult;
|
|
5198
|
-
}
|
|
5199
|
-
}
|
|
5200
|
-
}
|
|
5201
|
-
return { hasCycle: false };
|
|
5202
|
-
}
|
|
5203
|
-
function dfsVisit(fieldKey, graph, visitState, parent, path, bidirectionalPairs) {
|
|
5204
|
-
visitState.set(fieldKey, 1 /* VisitState.InProgress */);
|
|
5205
|
-
path.push(fieldKey);
|
|
5206
|
-
const node = graph.get(fieldKey);
|
|
5207
|
-
if (!node) {
|
|
5208
|
-
visitState.set(fieldKey, 2 /* VisitState.Completed */);
|
|
5209
|
-
path.pop();
|
|
5210
|
-
return null;
|
|
5211
|
-
}
|
|
5212
|
-
for (const targetFieldKey of node.dependedOnBy) {
|
|
5213
|
-
const targetState = visitState.get(targetFieldKey);
|
|
5214
|
-
if (targetState === 1 /* VisitState.InProgress */) {
|
|
5215
|
-
const cyclePath = extractCyclePath(path, targetFieldKey);
|
|
5216
|
-
if (isBidirectionalCycle(cyclePath, bidirectionalPairs)) {
|
|
5217
|
-
continue;
|
|
5218
|
-
}
|
|
5219
|
-
return {
|
|
5220
|
-
hasCycle: true,
|
|
5221
|
-
cyclePath,
|
|
5222
|
-
errorMessage: formatCycleError(cyclePath),
|
|
5223
|
-
};
|
|
5224
|
-
}
|
|
5225
|
-
if (targetState === 0 /* VisitState.Unvisited */) {
|
|
5226
|
-
parent.set(targetFieldKey, fieldKey);
|
|
5227
|
-
const result = dfsVisit(targetFieldKey, graph, visitState, parent, path, bidirectionalPairs);
|
|
5228
|
-
if (result) {
|
|
5229
|
-
return result;
|
|
5230
|
-
}
|
|
5231
|
-
}
|
|
5232
|
-
}
|
|
5233
|
-
visitState.set(fieldKey, 2 /* VisitState.Completed */);
|
|
5234
|
-
path.pop();
|
|
5235
|
-
return null;
|
|
5236
|
-
}
|
|
5237
|
-
function extractCyclePath(path, cycleStart) {
|
|
5238
|
-
const cycleStartIndex = path.indexOf(cycleStart);
|
|
5239
|
-
if (cycleStartIndex === -1) {
|
|
5240
|
-
return [...path, cycleStart];
|
|
5241
|
-
}
|
|
5242
|
-
const cyclePath = path.slice(cycleStartIndex);
|
|
5243
|
-
cyclePath.push(cycleStart); return cyclePath;
|
|
5244
|
-
}
|
|
5245
|
-
function formatCycleError(cyclePath) {
|
|
5246
|
-
const pathStr = cyclePath.join(' -> ');
|
|
5247
|
-
return (`Derivation cycle detected: ${pathStr}\n` +
|
|
5248
|
-
`This would cause an infinite loop at runtime. ` +
|
|
5249
|
-
`Remove one of the derivations to break the cycle.`);
|
|
5250
|
-
}
|
|
5251
|
-
function validateNoCycles(collection, logger) {
|
|
5252
|
-
const result = detectCycles(collection);
|
|
5253
|
-
if (result.hasCycle) {
|
|
5254
|
-
throw new Error(result.errorMessage);
|
|
5255
|
-
}
|
|
5256
|
-
if (DEV_MODE && result.bidirectionalPairs && result.bidirectionalPairs.length > 0 && logger) {
|
|
5257
|
-
logger.warn('Derivation: bidirectional derivation patterns detected. ' +
|
|
5258
|
-
'These patterns stabilize via equality checks, but may oscillate with floating-point values ' +
|
|
5259
|
-
'(e.g., currency conversions with rounding). ' +
|
|
5260
|
-
'Consider adding tolerance-based comparisons for numeric values.', result.bidirectionalPairs);
|
|
5261
|
-
}
|
|
5262
|
-
}
|
|
5263
|
-
|
|
5264
|
-
const LOG_PREFIX$3 = 'HTTP Derivation -';
|
|
5265
|
-
const DEFAULT_HTTP_DEBOUNCE_MS$1 = 300;
|
|
5266
|
-
function createHttpDerivationStream(entry, formValue$, context) {
|
|
5267
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5268
|
-
context.logger.warn(`${LOG_PREFIX$3} HTTP derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5269
|
-
`Array HTTP derivations will be supported in a future release.`);
|
|
5270
|
-
return EMPTY;
|
|
5271
|
-
}
|
|
5272
|
-
if (!entry.http || !entry.responseExpression) {
|
|
5273
|
-
return EMPTY;
|
|
5274
|
-
}
|
|
5275
|
-
const responseExpression = entry.responseExpression;
|
|
5276
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS$1;
|
|
5277
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5278
|
-
current: current,
|
|
5279
|
-
changedFields: getChangedKeys(previous, current),
|
|
5280
|
-
})),
|
|
5281
|
-
filter(({ changedFields }) => {
|
|
5282
|
-
if (changedFields.size === 0)
|
|
5283
|
-
return false;
|
|
5284
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5285
|
-
}), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
|
|
5286
|
-
return new Observable((subscriber) => {
|
|
5287
|
-
const formAccessor = untracked(() => context.form());
|
|
5288
|
-
if (entry.stopOnUserOverride) {
|
|
5289
|
-
const isDirty = readFieldDirty(formAccessor, entry.fieldKey);
|
|
5290
|
-
if (isDirty) {
|
|
5291
|
-
if (entry.reEngageOnDependencyChange && changedFields.size > 0) {
|
|
5292
|
-
if (entry.dependsOn.some((dep) => changedFields.has(dep))) {
|
|
5293
|
-
resetFieldState(formAccessor, entry.fieldKey);
|
|
5294
|
-
}
|
|
5295
|
-
else {
|
|
5296
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5297
|
-
derivationLogger.evaluation({
|
|
5298
|
-
debugName: entry.debugName,
|
|
5299
|
-
fieldKey: entry.fieldKey,
|
|
5300
|
-
result: 'skipped',
|
|
5301
|
-
skipReason: 'user-override',
|
|
5302
|
-
});
|
|
5303
|
-
subscriber.complete();
|
|
5304
|
-
return;
|
|
5305
|
-
}
|
|
5306
|
-
}
|
|
5307
|
-
else {
|
|
5308
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5309
|
-
derivationLogger.evaluation({
|
|
5310
|
-
debugName: entry.debugName,
|
|
5311
|
-
fieldKey: entry.fieldKey,
|
|
5312
|
-
result: 'skipped',
|
|
5313
|
-
skipReason: 'user-override',
|
|
5314
|
-
});
|
|
5315
|
-
subscriber.complete();
|
|
5316
|
-
return;
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
|
-
}
|
|
5320
|
-
const customFunctions = context.customFunctions?.();
|
|
5321
|
-
const externalData = context.externalData?.();
|
|
5322
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5323
|
-
const fieldAccessor = formAccessor[entry.fieldKey];
|
|
5324
|
-
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
5325
|
-
const evalContext = {
|
|
5326
|
-
fieldValue,
|
|
5327
|
-
formValue: current,
|
|
5328
|
-
fieldPath: entry.fieldKey,
|
|
5329
|
-
customFunctions,
|
|
5330
|
-
externalData,
|
|
5331
|
-
logger: context.logger,
|
|
5332
|
-
fieldState,
|
|
5333
|
-
formFieldState: createFormFieldStateMap(formAccessor, false),
|
|
5334
|
-
};
|
|
5335
|
-
if (entry.condition !== true) {
|
|
5336
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5337
|
-
if (!conditionMet) {
|
|
5338
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5339
|
-
derivationLogger.evaluation({
|
|
5340
|
-
debugName: entry.debugName,
|
|
5341
|
-
fieldKey: entry.fieldKey,
|
|
5342
|
-
result: 'skipped',
|
|
5343
|
-
skipReason: 'condition-false',
|
|
5344
|
-
});
|
|
5345
|
-
subscriber.complete();
|
|
5346
|
-
return;
|
|
5347
|
-
}
|
|
5348
|
-
}
|
|
5349
|
-
const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
|
|
5350
|
-
if (!resolvedRequest) {
|
|
5351
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5352
|
-
derivationLogger.evaluation({
|
|
5353
|
-
debugName: entry.debugName,
|
|
5354
|
-
fieldKey: entry.fieldKey,
|
|
5355
|
-
result: 'skipped',
|
|
5356
|
-
skipReason: 'condition-false',
|
|
5357
|
-
});
|
|
5358
|
-
subscriber.complete();
|
|
5359
|
-
return;
|
|
5360
|
-
}
|
|
5361
|
-
const method = (resolvedRequest.method ?? 'GET').toUpperCase();
|
|
5362
|
-
const httpSub = context.httpClient
|
|
5363
|
-
.request(method, resolvedRequest.url, {
|
|
5364
|
-
body: resolvedRequest.body,
|
|
5365
|
-
headers: resolvedRequest.headers,
|
|
5366
|
-
})
|
|
5367
|
-
.subscribe({
|
|
5368
|
-
next: (response) => {
|
|
5369
|
-
try {
|
|
5370
|
-
const newValue = ExpressionParser.evaluate(responseExpression, { response });
|
|
5371
|
-
const currentFormValue = untracked(() => context.formValue());
|
|
5372
|
-
const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
|
|
5373
|
-
if (isEqual(currentValue, newValue)) {
|
|
5374
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5375
|
-
derivationLogger.evaluation({
|
|
5376
|
-
debugName: entry.debugName,
|
|
5377
|
-
fieldKey: entry.fieldKey,
|
|
5378
|
-
result: 'skipped',
|
|
5379
|
-
skipReason: 'value-unchanged',
|
|
5380
|
-
});
|
|
5381
|
-
subscriber.complete();
|
|
5382
|
-
return;
|
|
5383
|
-
}
|
|
5384
|
-
const currentForm = untracked(() => context.form());
|
|
5385
|
-
if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
|
|
5386
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5387
|
-
derivationLogger.evaluation({
|
|
5388
|
-
debugName: entry.debugName,
|
|
5389
|
-
fieldKey: entry.fieldKey,
|
|
5390
|
-
result: 'skipped',
|
|
5391
|
-
skipReason: 'user-override',
|
|
5392
|
-
});
|
|
5393
|
-
subscriber.complete();
|
|
5394
|
-
return;
|
|
5395
|
-
}
|
|
5396
|
-
applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
|
|
5397
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5398
|
-
derivationLogger.evaluation({
|
|
5399
|
-
debugName: entry.debugName,
|
|
5400
|
-
fieldKey: entry.fieldKey,
|
|
5401
|
-
result: 'applied',
|
|
5402
|
-
previousValue: currentValue,
|
|
5403
|
-
newValue,
|
|
5404
|
-
});
|
|
5405
|
-
subscriber.complete();
|
|
5406
|
-
}
|
|
5407
|
-
catch (error) {
|
|
5408
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5409
|
-
context.logger.warn(`${LOG_PREFIX$3} Failed to process response for '${entry.fieldKey}': ${message}`);
|
|
5410
|
-
subscriber.complete();
|
|
5411
|
-
}
|
|
5412
|
-
},
|
|
5413
|
-
error: (error) => {
|
|
5414
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5415
|
-
context.logger.warn(`${LOG_PREFIX$3} HTTP request failed for '${entry.fieldKey}': ${message}`);
|
|
5416
|
-
subscriber.complete();
|
|
5417
|
-
},
|
|
5418
|
-
});
|
|
5419
|
-
return () => {
|
|
5420
|
-
httpSub.unsubscribe();
|
|
5421
|
-
};
|
|
5422
|
-
});
|
|
5423
|
-
}), catchError$1((error) => {
|
|
5424
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5425
|
-
context.logger.warn(`${LOG_PREFIX$3} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
5426
|
-
return EMPTY;
|
|
5427
|
-
}));
|
|
5428
|
-
}
|
|
5429
|
-
|
|
5430
|
-
const LOG_PREFIX$2 = 'Async Derivation -';
|
|
5431
|
-
const DEFAULT_ASYNC_DEBOUNCE_MS$1 = 300;
|
|
5432
|
-
function createAsyncDerivationStream(entry, formValue$, context) {
|
|
5433
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5434
|
-
context.logger.warn(`${LOG_PREFIX$2} Async derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5435
|
-
`Array async derivations will be supported in a future release.`);
|
|
5436
|
-
return EMPTY;
|
|
5437
|
-
}
|
|
5438
|
-
if (!entry.asyncFunctionName && !entry.asyncFn) {
|
|
5439
|
-
context.logger.warn(`${LOG_PREFIX$2} Derivation for '${entry.fieldKey}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
5440
|
-
`One of the two is required for source: 'asyncFunction'.`);
|
|
5441
|
-
return EMPTY;
|
|
5442
|
-
}
|
|
5443
|
-
if (entry.asyncFn && entry.asyncFunctionName) {
|
|
5444
|
-
context.logger.warn(`${LOG_PREFIX$2} Both "asyncFn" and "asyncFunctionName" are set on derivation for '${entry.fieldKey}'. ` +
|
|
5445
|
-
`Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
|
|
5446
|
-
}
|
|
5447
|
-
const inlineAsyncFn = entry.asyncFn;
|
|
5448
|
-
const asyncFunctionName = entry.asyncFunctionName;
|
|
5449
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS$1;
|
|
5450
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5451
|
-
current: current,
|
|
5452
|
-
changedFields: getChangedKeys(previous, current),
|
|
5453
|
-
})),
|
|
5454
|
-
filter(({ changedFields }) => {
|
|
5455
|
-
if (changedFields.size === 0)
|
|
5456
|
-
return false;
|
|
5457
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5458
|
-
}), debounceTime(debounceMs), switchMap(({ current, changedFields }) => {
|
|
5459
|
-
return new Observable((subscriber) => {
|
|
5460
|
-
const formAccessor = untracked(() => context.form());
|
|
5461
|
-
if (entry.stopOnUserOverride) {
|
|
5462
|
-
const isDirty = readFieldDirty(formAccessor, entry.fieldKey);
|
|
5463
|
-
if (isDirty) {
|
|
5464
|
-
if (entry.reEngageOnDependencyChange) {
|
|
5465
|
-
resetFieldState(formAccessor, entry.fieldKey);
|
|
5466
|
-
}
|
|
5467
|
-
else {
|
|
5468
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5469
|
-
derivationLogger.evaluation({
|
|
5470
|
-
debugName: entry.debugName,
|
|
5471
|
-
fieldKey: entry.fieldKey,
|
|
5472
|
-
result: 'skipped',
|
|
5473
|
-
skipReason: 'user-override',
|
|
5474
|
-
});
|
|
5475
|
-
subscriber.complete();
|
|
5476
|
-
return;
|
|
5477
|
-
}
|
|
5478
|
-
}
|
|
5479
|
-
}
|
|
5480
|
-
const customFunctions = context.customFunctions?.();
|
|
5481
|
-
const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
|
|
5482
|
-
const externalData = context.externalData?.();
|
|
5483
|
-
const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
|
|
5484
|
-
if (!asyncFn) {
|
|
5485
|
-
context.logger.warn(`${LOG_PREFIX$2} Async derivation function '${asyncFunctionName}' not found for field '${entry.fieldKey}'. ` +
|
|
5486
|
-
`Register it in customFnConfig.asyncDerivations.`);
|
|
5487
|
-
subscriber.complete();
|
|
5488
|
-
return;
|
|
5489
|
-
}
|
|
5490
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5491
|
-
const fieldAccessor = formAccessor[entry.fieldKey];
|
|
5492
|
-
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
5493
|
-
const evalContext = {
|
|
5494
|
-
fieldValue,
|
|
5495
|
-
formValue: current,
|
|
5496
|
-
fieldPath: entry.fieldKey,
|
|
5497
|
-
customFunctions,
|
|
5498
|
-
externalData,
|
|
5499
|
-
logger: context.logger,
|
|
5500
|
-
fieldState,
|
|
5501
|
-
formFieldState: createFormFieldStateMap(formAccessor, false),
|
|
5502
|
-
};
|
|
5503
|
-
if (entry.condition !== true) {
|
|
5504
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5505
|
-
if (!conditionMet) {
|
|
5506
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5507
|
-
derivationLogger.evaluation({
|
|
5508
|
-
debugName: entry.debugName,
|
|
5509
|
-
fieldKey: entry.fieldKey,
|
|
5510
|
-
result: 'skipped',
|
|
5511
|
-
skipReason: 'condition-false',
|
|
5512
|
-
});
|
|
5513
|
-
subscriber.complete();
|
|
5514
|
-
return;
|
|
5515
|
-
}
|
|
5516
|
-
}
|
|
5517
|
-
const result$ = from(asyncFn(evalContext));
|
|
5518
|
-
const asyncSub = result$.subscribe({
|
|
5519
|
-
next: (newValue) => {
|
|
5520
|
-
try {
|
|
5521
|
-
const currentFormValue = untracked(() => context.formValue());
|
|
5522
|
-
const currentValue = getNestedValue(currentFormValue, entry.fieldKey);
|
|
5523
|
-
if (isEqual(currentValue, newValue)) {
|
|
5524
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5525
|
-
derivationLogger.evaluation({
|
|
5526
|
-
debugName: entry.debugName,
|
|
5527
|
-
fieldKey: entry.fieldKey,
|
|
5528
|
-
result: 'skipped',
|
|
5529
|
-
skipReason: 'value-unchanged',
|
|
5530
|
-
});
|
|
5531
|
-
subscriber.complete();
|
|
5532
|
-
return;
|
|
5533
|
-
}
|
|
5534
|
-
const currentForm = untracked(() => context.form());
|
|
5535
|
-
if (entry.stopOnUserOverride && readFieldDirty(currentForm, entry.fieldKey)) {
|
|
5536
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5537
|
-
derivationLogger.evaluation({
|
|
5538
|
-
debugName: entry.debugName,
|
|
5539
|
-
fieldKey: entry.fieldKey,
|
|
5540
|
-
result: 'skipped',
|
|
5541
|
-
skipReason: 'user-override',
|
|
5542
|
-
});
|
|
5543
|
-
subscriber.complete();
|
|
5544
|
-
return;
|
|
5545
|
-
}
|
|
5546
|
-
applyValueToForm(entry.fieldKey, newValue, currentForm, context.logger, context.warningTracker);
|
|
5547
|
-
const derivationLogger = untracked(() => context.derivationLogger());
|
|
5548
|
-
derivationLogger.evaluation({
|
|
5549
|
-
debugName: entry.debugName,
|
|
5550
|
-
fieldKey: entry.fieldKey,
|
|
5551
|
-
result: 'applied',
|
|
5552
|
-
previousValue: currentValue,
|
|
5553
|
-
newValue,
|
|
5554
|
-
});
|
|
5555
|
-
subscriber.complete();
|
|
5556
|
-
}
|
|
5557
|
-
catch (error) {
|
|
5558
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5559
|
-
context.logger.warn(`${LOG_PREFIX$2} Failed to process result for '${entry.fieldKey}': ${message}`);
|
|
5560
|
-
subscriber.complete();
|
|
5561
|
-
}
|
|
5562
|
-
},
|
|
5563
|
-
error: (error) => {
|
|
5564
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5565
|
-
context.logger.warn(`${LOG_PREFIX$2} Async function failed for '${entry.fieldKey}': ${message}`);
|
|
5566
|
-
subscriber.complete();
|
|
5567
|
-
},
|
|
5568
|
-
complete: () => {
|
|
5569
|
-
subscriber.complete();
|
|
5570
|
-
},
|
|
5571
|
-
});
|
|
5572
|
-
return () => {
|
|
5573
|
-
asyncSub.unsubscribe();
|
|
5574
|
-
};
|
|
5575
|
-
});
|
|
5576
|
-
}), catchError$1((error) => {
|
|
5577
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5578
|
-
context.logger.warn(`${LOG_PREFIX$2} Unexpected stream error for '${entry.fieldKey}': ${message}`);
|
|
5579
|
-
return EMPTY;
|
|
5580
|
-
}));
|
|
5581
|
-
}
|
|
5582
|
-
|
|
5583
|
-
function resolveExternalData(externalDataSignal) {
|
|
5584
|
-
const externalDataRecord = untracked(() => externalDataSignal?.());
|
|
5585
|
-
if (!externalDataRecord)
|
|
5586
|
-
return undefined;
|
|
5587
|
-
const resolved = {};
|
|
5588
|
-
for (const [key, value] of Object.entries(externalDataRecord)) {
|
|
5589
|
-
if (isSignal(value)) {
|
|
5590
|
-
resolved[key] = untracked(() => value());
|
|
5591
|
-
}
|
|
5592
|
-
else {
|
|
5593
|
-
resolved[key] = value;
|
|
5594
|
-
}
|
|
5595
|
-
}
|
|
5596
|
-
return resolved;
|
|
5597
|
-
}
|
|
5598
|
-
|
|
5599
|
-
function getDebouncePeriods(entries) {
|
|
5600
|
-
const periods = new Set();
|
|
5601
|
-
for (const entry of entries) {
|
|
5602
|
-
if (entry.trigger === 'debounced') {
|
|
5603
|
-
periods.add(entry.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
5604
|
-
}
|
|
5605
|
-
}
|
|
5606
|
-
return Array.from(periods);
|
|
5607
|
-
}
|
|
5608
|
-
function filterEntriesByDebouncePeriod(entries, period) {
|
|
5609
|
-
return entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === period);
|
|
5610
|
-
}
|
|
5611
|
-
|
|
5612
|
-
function entrySetsEqual(prev, next, sig) {
|
|
5613
|
-
if (prev.length !== next.length)
|
|
5614
|
-
return false;
|
|
5615
|
-
const counts = new Map();
|
|
5616
|
-
for (const item of prev) {
|
|
5617
|
-
const key = sig(item);
|
|
5618
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
5619
|
-
}
|
|
5620
|
-
for (const item of next) {
|
|
5621
|
-
const key = sig(item);
|
|
5622
|
-
const remaining = counts.get(key);
|
|
5623
|
-
if (!remaining)
|
|
5624
|
-
return false;
|
|
5625
|
-
if (remaining === 1)
|
|
5626
|
-
counts.delete(key);
|
|
5627
|
-
else
|
|
5628
|
-
counts.set(key, remaining - 1);
|
|
5629
|
-
}
|
|
5630
|
-
return counts.size === 0;
|
|
5631
|
-
}
|
|
5632
|
-
function buildEntryStreamPipeline(config) {
|
|
5633
|
-
return config.collection$.pipe(map((collection) => config.selectEntries(collection)), distinctUntilChanged((prev, next) => entrySetsEqual(prev, next, config.entrySignature)), switchMap((entries) => {
|
|
5634
|
-
if (entries.length === 0)
|
|
5635
|
-
return EMPTY;
|
|
5636
|
-
const streams = entries.map((entry) => config.createStream(entry)).filter((stream) => stream !== null);
|
|
5637
|
-
if (streams.length === 0)
|
|
5638
|
-
return EMPTY;
|
|
5639
|
-
return merge(...streams);
|
|
5640
|
-
}));
|
|
5641
|
-
}
|
|
5642
|
-
|
|
5643
|
-
function setupOnChangeStream(opts) {
|
|
5644
|
-
const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
|
|
5645
|
-
const formValue$ = toObservable(opts.formValueSignal, { injector: opts.injector });
|
|
5646
|
-
const extra$ = opts.additionalAwakeners?.length ? opts.additionalAwakeners.map((s) => toObservable(s, { injector: opts.injector })) : [];
|
|
5647
|
-
collection$
|
|
5648
|
-
.pipe(filter((collection) => collection !== null), combineLatestWith(formValue$, ...extra$), auditTime(0), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({
|
|
5649
|
-
collection: current[0],
|
|
5650
|
-
changedFields: getChangedKeys(previous?.[1] ?? null, current[1]),
|
|
5651
|
-
})), exhaustMap(({ collection, changedFields }) => {
|
|
5652
|
-
opts.applyOnChange(collection, changedFields);
|
|
5653
|
-
return scheduled([null], queueScheduler);
|
|
5654
|
-
}), takeUntilDestroyed(opts.destroyRef))
|
|
5655
|
-
.subscribe({
|
|
5656
|
-
error: (err) => opts.logger.error(opts.errorLabel, err),
|
|
5657
|
-
});
|
|
5658
|
-
}
|
|
5659
|
-
function setupDebouncedStream(opts) {
|
|
5660
|
-
toObservable(opts.formValueSignal, { injector: opts.injector })
|
|
5661
|
-
.pipe(debounceTime(DEFAULT_DEBOUNCE_MS), startWith(null), pairwise(), filter((pair) => pair[1] !== null), map(([previous, current]) => ({ current, changedFields: getChangedKeys(previous, current) })), filter(({ changedFields }) => changedFields.size > 0), switchMap(({ changedFields }) => {
|
|
5662
|
-
const collection = untracked(() => opts.collectionSignal());
|
|
5663
|
-
if (!collection)
|
|
5664
|
-
return of(null);
|
|
5665
|
-
const debouncePeriods = getDebouncePeriods(collection.entries);
|
|
5666
|
-
if (debouncePeriods.length === 0)
|
|
5667
|
-
return of(null);
|
|
5668
|
-
const periodStreams = debouncePeriods.map((debounceMs) => {
|
|
5669
|
-
const additionalWait = Math.max(0, debounceMs - DEFAULT_DEBOUNCE_MS);
|
|
5670
|
-
if (additionalWait === 0) {
|
|
5671
|
-
return of(null).pipe(map(() => {
|
|
5672
|
-
opts.applyDebouncedForPeriod(debounceMs, collection, changedFields);
|
|
5673
|
-
}));
|
|
5674
|
-
}
|
|
5675
|
-
return timer(additionalWait).pipe(map(() => {
|
|
5676
|
-
const fresh = untracked(() => opts.collectionSignal()) ?? collection;
|
|
5677
|
-
opts.applyDebouncedForPeriod(debounceMs, fresh, changedFields);
|
|
5678
|
-
}));
|
|
5679
|
-
});
|
|
5680
|
-
return merge(...periodStreams);
|
|
5681
|
-
}), takeUntilDestroyed(opts.destroyRef))
|
|
5682
|
-
.subscribe({
|
|
5683
|
-
error: (err) => opts.logger.error(opts.errorLabel, err),
|
|
5684
|
-
});
|
|
5685
|
-
}
|
|
5686
|
-
function setupEntryAsyncStream(opts) {
|
|
5687
|
-
const collection$ = toObservable(opts.collectionSignal, { injector: opts.injector });
|
|
5688
|
-
buildEntryStreamPipeline({
|
|
5689
|
-
collection$,
|
|
5690
|
-
selectEntries: opts.selectEntries,
|
|
5691
|
-
entrySignature: opts.entrySignature,
|
|
5692
|
-
createStream: opts.createStream,
|
|
5693
|
-
})
|
|
5694
|
-
.pipe(catchError((err) => {
|
|
5695
|
-
opts.logger.error(opts.errorLabel, err);
|
|
5696
|
-
return EMPTY;
|
|
5697
|
-
}), takeUntilDestroyed(opts.destroyRef))
|
|
5698
|
-
.subscribe();
|
|
5699
|
-
}
|
|
5700
|
-
|
|
5701
|
-
function createEmptyPropertyDerivationCollection() {
|
|
5702
|
-
return { entries: [] };
|
|
5703
|
-
}
|
|
5704
|
-
function isAsyncPropertyDerivationEntry(entry) {
|
|
5705
|
-
return Boolean(entry.http) || Boolean(entry.asyncFunctionName) || Boolean(entry.asyncFn);
|
|
5706
|
-
}
|
|
5707
|
-
|
|
5708
|
-
const ERROR_PREFIX = '[PropertyDerivation]';
|
|
5709
|
-
function applyPropertyDerivations(collection, context, changedFields) {
|
|
5710
|
-
let appliedCount = 0;
|
|
5711
|
-
let skippedCount = 0;
|
|
5712
|
-
let errorCount = 0;
|
|
5713
|
-
const baseEntries = changedFields ? getEntriesForChangedFields(collection.entries, changedFields) : collection.entries;
|
|
5714
|
-
const entriesToProcess = baseEntries.filter((entry) => !isAsyncPropertyDerivationEntry(entry));
|
|
5715
|
-
for (const entry of entriesToProcess) {
|
|
5716
|
-
try {
|
|
5717
|
-
const applied = tryApplyPropertyDerivation(entry, context);
|
|
5718
|
-
if (applied) {
|
|
5719
|
-
appliedCount++;
|
|
5720
|
-
}
|
|
5721
|
-
else {
|
|
5722
|
-
skippedCount++;
|
|
5723
|
-
}
|
|
5724
|
-
}
|
|
5725
|
-
catch (error) {
|
|
5726
|
-
errorCount++;
|
|
5727
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5728
|
-
context.logger.error(`${ERROR_PREFIX} Failed to process property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}): ${errorMessage}`);
|
|
5729
|
-
}
|
|
5730
|
-
}
|
|
5731
|
-
return { appliedCount, skippedCount, errorCount };
|
|
5732
|
-
}
|
|
5733
|
-
function applyPropertyDerivationsForTrigger(collection, trigger, context, changedFields) {
|
|
5734
|
-
const filteredEntries = collection.entries.filter((entry) => {
|
|
5735
|
-
if (trigger === 'onChange') {
|
|
5736
|
-
return !entry.trigger || entry.trigger === 'onChange';
|
|
5737
|
-
}
|
|
5738
|
-
return entry.trigger === trigger;
|
|
5739
|
-
});
|
|
5740
|
-
const filteredCollection = { entries: filteredEntries };
|
|
5741
|
-
return applyPropertyDerivations(filteredCollection, context, changedFields);
|
|
5742
|
-
}
|
|
5743
|
-
function getEntriesForChangedFields(entries, changedFields) {
|
|
5744
|
-
return entries.filter((entry) => {
|
|
5745
|
-
if (entry.dependsOn.includes('*'))
|
|
5746
|
-
return true;
|
|
5747
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5748
|
-
});
|
|
5749
|
-
}
|
|
5750
|
-
function tryApplyPropertyDerivation(entry, context) {
|
|
5751
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5752
|
-
return tryApplyArrayPropertyDerivation(entry, context);
|
|
5753
|
-
}
|
|
5754
|
-
const formValue = untracked(() => context.formValue());
|
|
5755
|
-
const evalContext = createEvaluationContext(entry, formValue, context);
|
|
5756
|
-
if (!evaluatePropertyCondition(entry.condition, evalContext)) {
|
|
5757
|
-
return false;
|
|
5758
|
-
}
|
|
5759
|
-
const newValue = computePropertyValue(entry, evalContext, context);
|
|
5760
|
-
context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue);
|
|
5761
|
-
return true;
|
|
5762
|
-
}
|
|
5763
|
-
function tryApplyArrayPropertyDerivation(entry, context) {
|
|
5764
|
-
const formValue = untracked(() => context.formValue());
|
|
5765
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
5766
|
-
if (!pathInfo.isArrayPath) {
|
|
5767
|
-
return false;
|
|
5768
|
-
}
|
|
5769
|
-
const { arrayPath } = pathInfo;
|
|
5770
|
-
const arrayValue = getNestedValue(formValue, arrayPath);
|
|
5771
|
-
if (!Array.isArray(arrayValue)) {
|
|
5772
|
-
return false;
|
|
5773
|
-
}
|
|
5774
|
-
let appliedAny = false;
|
|
5775
|
-
for (let i = 0; i < arrayValue.length; i++) {
|
|
5776
|
-
const resolvedPath = resolveArrayPath(entry.fieldKey, i);
|
|
5777
|
-
const arrayItem = arrayValue[i];
|
|
5778
|
-
const evalContext = createArrayItemEvaluationContext(entry, arrayItem, formValue, i, arrayPath, context);
|
|
5779
|
-
if (!evaluatePropertyCondition(entry.condition, evalContext)) {
|
|
5780
|
-
continue;
|
|
5781
|
-
}
|
|
5782
|
-
try {
|
|
5783
|
-
const newValue = computePropertyValue(entry, evalContext, context);
|
|
5784
|
-
context.store.setOverride(resolvedPath, entry.targetProperty, newValue);
|
|
5785
|
-
appliedAny = true;
|
|
5786
|
-
}
|
|
5787
|
-
catch (error) {
|
|
5788
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5789
|
-
context.logger.error(`${ERROR_PREFIX} Failed to compute property derivation (field: ${entry.fieldKey}, property: ${entry.targetProperty}, index: ${i}): ${errorMessage}`);
|
|
5790
|
-
}
|
|
5791
|
-
}
|
|
5792
|
-
return appliedAny;
|
|
5793
|
-
}
|
|
5794
|
-
function createEvaluationContext(entry, formValue, context) {
|
|
5795
|
-
const fieldValue = getNestedValue(formValue, entry.fieldKey);
|
|
5796
|
-
const parentPath = getParentPathInScope(entry.fieldKey);
|
|
5797
|
-
const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
|
|
5798
|
-
return {
|
|
5799
|
-
fieldValue,
|
|
5800
|
-
formValue,
|
|
5801
|
-
fieldPath: entry.fieldKey,
|
|
5802
|
-
groupValue,
|
|
5803
|
-
customFunctions: context.customFunctions,
|
|
5804
|
-
externalData: context.externalData,
|
|
5805
|
-
logger: context.logger,
|
|
5806
|
-
};
|
|
5807
|
-
}
|
|
5808
|
-
function createArrayItemEvaluationContext(entry, arrayItem, rootFormValue, itemIndex, arrayPath, context) {
|
|
5809
|
-
const pathInfo = parseArrayPath(entry.fieldKey);
|
|
5810
|
-
const { relativePath, groupValue, fieldValue } = resolveArrayItemScope(pathInfo, arrayItem);
|
|
5811
|
-
return {
|
|
5812
|
-
fieldValue,
|
|
5813
|
-
formValue: arrayItem,
|
|
5814
|
-
fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
|
|
5815
|
-
groupValue,
|
|
5816
|
-
customFunctions: context.customFunctions,
|
|
5817
|
-
externalData: context.externalData,
|
|
5818
|
-
logger: context.logger,
|
|
5819
|
-
rootFormValue,
|
|
5820
|
-
arrayIndex: itemIndex,
|
|
5821
|
-
arrayPath,
|
|
5822
|
-
};
|
|
5823
|
-
}
|
|
5824
|
-
function evaluatePropertyCondition(condition, context) {
|
|
5825
|
-
if (typeof condition === 'boolean') {
|
|
5826
|
-
return condition;
|
|
5827
|
-
}
|
|
5828
|
-
return evaluateCondition(condition, context);
|
|
5829
|
-
}
|
|
5830
|
-
function computePropertyValue(entry, evalContext, applicatorContext) {
|
|
5831
|
-
return computeValueFromEntry(entry, evalContext, {
|
|
5832
|
-
derivationFunctions: applicatorContext.derivationFunctions,
|
|
5833
|
-
subject: `${entry.fieldKey}.${entry.targetProperty}`,
|
|
5834
|
-
functionKind: 'Property derivation function',
|
|
5835
|
-
});
|
|
5836
|
-
}
|
|
5837
|
-
|
|
5838
|
-
const LOG_PREFIX$1 = 'HTTP Property Derivation -';
|
|
5839
|
-
const DEFAULT_HTTP_DEBOUNCE_MS = 300;
|
|
5840
|
-
function createHttpPropertyDerivationStream(entry, formValue$, context) {
|
|
5841
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5842
|
-
context.logger.warn(`${LOG_PREFIX$1} HTTP property derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5843
|
-
`Array HTTP property derivations will be supported in a future release.`);
|
|
5844
|
-
return EMPTY;
|
|
5845
|
-
}
|
|
5846
|
-
if (!entry.http || !entry.responseExpression) {
|
|
5847
|
-
return EMPTY;
|
|
5848
|
-
}
|
|
5849
|
-
const responseExpression = entry.responseExpression;
|
|
5850
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_HTTP_DEBOUNCE_MS;
|
|
5851
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5852
|
-
current: current,
|
|
5853
|
-
changedFields: getChangedKeys(previous, current),
|
|
5854
|
-
})), filter(({ changedFields }) => {
|
|
5855
|
-
if (changedFields.size === 0)
|
|
5856
|
-
return false;
|
|
5857
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5858
|
-
}), debounceTime(debounceMs), switchMap(({ current }) => {
|
|
5859
|
-
return new Observable((subscriber) => {
|
|
5860
|
-
const customFunctions = context.customFunctions?.();
|
|
5861
|
-
const externalData = context.externalData?.();
|
|
5862
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5863
|
-
const evalContext = {
|
|
5864
|
-
fieldValue,
|
|
5865
|
-
formValue: current,
|
|
5866
|
-
fieldPath: entry.fieldKey,
|
|
5867
|
-
customFunctions,
|
|
5868
|
-
externalData,
|
|
5869
|
-
logger: context.logger,
|
|
5870
|
-
};
|
|
5871
|
-
if (entry.condition !== true) {
|
|
5872
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5873
|
-
if (!conditionMet) {
|
|
5874
|
-
subscriber.complete();
|
|
5875
|
-
return;
|
|
5876
|
-
}
|
|
5877
|
-
}
|
|
5878
|
-
const resolvedRequest = resolveHttpRequest(entry.http, evalContext);
|
|
5879
|
-
if (!resolvedRequest) {
|
|
5880
|
-
subscriber.complete();
|
|
5881
|
-
return;
|
|
5882
|
-
}
|
|
5883
|
-
const method = (resolvedRequest.method ?? 'GET').toUpperCase();
|
|
5884
|
-
const httpSub = context.httpClient
|
|
5885
|
-
.request(method, resolvedRequest.url, {
|
|
5886
|
-
body: resolvedRequest.body,
|
|
5887
|
-
headers: resolvedRequest.headers,
|
|
5888
|
-
})
|
|
5889
|
-
.subscribe({
|
|
5890
|
-
next: (response) => {
|
|
5891
|
-
try {
|
|
5892
|
-
const newValue = ExpressionParser.evaluate(responseExpression, { response });
|
|
5893
|
-
untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
|
|
5894
|
-
subscriber.complete();
|
|
5895
|
-
}
|
|
5896
|
-
catch (error) {
|
|
5897
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5898
|
-
context.logger.warn(`${LOG_PREFIX$1} Failed to process response for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5899
|
-
subscriber.complete();
|
|
5900
|
-
}
|
|
5901
|
-
},
|
|
5902
|
-
error: (error) => {
|
|
5903
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5904
|
-
context.logger.warn(`${LOG_PREFIX$1} HTTP request failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5905
|
-
subscriber.complete();
|
|
5906
|
-
},
|
|
5907
|
-
});
|
|
5908
|
-
return () => {
|
|
5909
|
-
httpSub.unsubscribe();
|
|
5910
|
-
};
|
|
5911
|
-
});
|
|
5912
|
-
}), catchError$1((error) => {
|
|
5913
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5914
|
-
context.logger.warn(`${LOG_PREFIX$1} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5915
|
-
return EMPTY;
|
|
5916
|
-
}));
|
|
5917
|
-
}
|
|
5918
|
-
|
|
5919
|
-
const LOG_PREFIX = 'Async Property Derivation -';
|
|
5920
|
-
const DEFAULT_ASYNC_DEBOUNCE_MS = 300;
|
|
5921
|
-
function createAsyncPropertyDerivationStream(entry, formValue$, context) {
|
|
5922
|
-
if (isArrayPlaceholderPath(entry.fieldKey)) {
|
|
5923
|
-
context.logger.warn(`${LOG_PREFIX} Async property derivation for array field '${entry.fieldKey}' is not supported. ` +
|
|
5924
|
-
`Array async property derivations will be supported in a future release.`);
|
|
5925
|
-
return EMPTY;
|
|
5926
|
-
}
|
|
5927
|
-
if (!entry.asyncFunctionName && !entry.asyncFn) {
|
|
5928
|
-
context.logger.warn(`${LOG_PREFIX} Property derivation for '${entry.fieldKey}.${entry.targetProperty}' has neither "asyncFunctionName" nor "asyncFn". ` +
|
|
5929
|
-
`One of the two is required for source: 'asyncFunction'.`);
|
|
5930
|
-
return EMPTY;
|
|
5931
|
-
}
|
|
5932
|
-
if (entry.asyncFn && entry.asyncFunctionName) {
|
|
5933
|
-
context.logger.warn(`${LOG_PREFIX} Both "asyncFn" and "asyncFunctionName" are set on property derivation for '${entry.fieldKey}.${entry.targetProperty}'. ` +
|
|
5934
|
-
`Inline "asyncFn" takes precedence; "asyncFunctionName" is ignored.`);
|
|
5935
|
-
}
|
|
5936
|
-
const inlineAsyncFn = entry.asyncFn;
|
|
5937
|
-
const asyncFunctionName = entry.asyncFunctionName;
|
|
5938
|
-
const debounceMs = entry.debounceMs ?? DEFAULT_ASYNC_DEBOUNCE_MS;
|
|
5939
|
-
return formValue$.pipe(startWith(null), pairwise(), map(([previous, current]) => ({
|
|
5940
|
-
current: current,
|
|
5941
|
-
changedFields: getChangedKeys(previous, current),
|
|
5942
|
-
})), filter(({ changedFields }) => {
|
|
5943
|
-
if (changedFields.size === 0)
|
|
5944
|
-
return false;
|
|
5945
|
-
return entry.dependsOn.some((dep) => changedFields.has(dep));
|
|
5946
|
-
}), debounceTime(debounceMs), switchMap(({ current }) => {
|
|
5947
|
-
return new Observable((subscriber) => {
|
|
5948
|
-
const customFunctions = context.customFunctions?.();
|
|
5949
|
-
const asyncDerivationFunctions = context.asyncDerivationFunctions?.();
|
|
5950
|
-
const externalData = context.externalData?.();
|
|
5951
|
-
const asyncFn = inlineAsyncFn ?? (asyncFunctionName ? asyncDerivationFunctions?.[asyncFunctionName] : undefined);
|
|
5952
|
-
if (!asyncFn) {
|
|
5953
|
-
context.logger.warn(`${LOG_PREFIX} Async derivation function '${asyncFunctionName}' not found for '${entry.fieldKey}.${entry.targetProperty}'. ` +
|
|
5954
|
-
`Register it in customFnConfig.asyncDerivations.`);
|
|
5955
|
-
subscriber.complete();
|
|
5956
|
-
return;
|
|
5957
|
-
}
|
|
5958
|
-
const fieldValue = getNestedValue(current, entry.fieldKey);
|
|
5959
|
-
const evalContext = {
|
|
5960
|
-
fieldValue,
|
|
5961
|
-
formValue: current,
|
|
5962
|
-
fieldPath: entry.fieldKey,
|
|
5963
|
-
customFunctions,
|
|
5964
|
-
externalData,
|
|
5965
|
-
logger: context.logger,
|
|
5966
|
-
};
|
|
5967
|
-
if (entry.condition !== true) {
|
|
5968
|
-
const conditionMet = typeof entry.condition === 'boolean' ? entry.condition : evaluateCondition(entry.condition, evalContext);
|
|
5969
|
-
if (!conditionMet) {
|
|
5970
|
-
subscriber.complete();
|
|
5971
|
-
return;
|
|
5972
|
-
}
|
|
5973
|
-
}
|
|
5974
|
-
const result$ = from(asyncFn(evalContext));
|
|
5975
|
-
const asyncSub = result$.subscribe({
|
|
5976
|
-
next: (newValue) => {
|
|
5977
|
-
try {
|
|
5978
|
-
untracked(() => context.store.setOverride(entry.fieldKey, entry.targetProperty, newValue));
|
|
5979
|
-
subscriber.complete();
|
|
5980
|
-
}
|
|
5981
|
-
catch (error) {
|
|
5982
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5983
|
-
context.logger.warn(`${LOG_PREFIX} Failed to process result for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5984
|
-
subscriber.complete();
|
|
5985
|
-
}
|
|
5986
|
-
},
|
|
5987
|
-
error: (error) => {
|
|
5988
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5989
|
-
context.logger.warn(`${LOG_PREFIX} Async function failed for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
5990
|
-
subscriber.complete();
|
|
5991
|
-
},
|
|
5992
|
-
complete: () => {
|
|
5993
|
-
subscriber.complete();
|
|
5994
|
-
},
|
|
5995
|
-
});
|
|
5996
|
-
return () => {
|
|
5997
|
-
asyncSub.unsubscribe();
|
|
5998
|
-
};
|
|
5999
|
-
});
|
|
6000
|
-
}), catchError$1((error) => {
|
|
6001
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
6002
|
-
context.logger.warn(`${LOG_PREFIX} Unexpected stream error for '${entry.fieldKey}.${entry.targetProperty}': ${message}`);
|
|
6003
|
-
return EMPTY;
|
|
6004
|
-
}));
|
|
6005
|
-
}
|
|
6006
|
-
|
|
6007
|
-
class DerivationOrchestrator {
|
|
6008
|
-
config;
|
|
6009
|
-
injector = inject(Injector);
|
|
6010
|
-
destroyRef = inject(DestroyRef);
|
|
6011
|
-
logger = inject(DynamicFormLogger);
|
|
6012
|
-
warningTracker = inject(DERIVATION_WARNING_TRACKER);
|
|
6013
|
-
functionRegistry = inject(FunctionRegistryService);
|
|
6014
|
-
httpClient = inject(HttpClient, { optional: true });
|
|
6015
|
-
formOptions;
|
|
6016
|
-
derivationCollection;
|
|
6017
|
-
propertyDerivationCollection;
|
|
6018
|
-
constructor(config) {
|
|
6019
|
-
this.config = config;
|
|
6020
|
-
const valueEnabled = !!(config.form && config.derivationLogger);
|
|
6021
|
-
const propertyStore = config.propertyStore;
|
|
6022
|
-
const propertyEnabled = !!propertyStore;
|
|
6023
|
-
const sharedAllDerivations = valueEnabled && propertyEnabled
|
|
6024
|
-
? computed(() => {
|
|
6025
|
-
const fields = config.schemaFields();
|
|
6026
|
-
if (!fields || fields.length === 0)
|
|
6027
|
-
return null;
|
|
6028
|
-
return collectAllDerivations(fields);
|
|
6029
|
-
})
|
|
6030
|
-
: null;
|
|
6031
|
-
this.derivationCollection = valueEnabled
|
|
6032
|
-
? this.setupValuePipeline(sharedAllDerivations)
|
|
6033
|
-
: computed(() => null);
|
|
6034
|
-
this.propertyDerivationCollection = propertyStore
|
|
6035
|
-
? this.setupPropertyPipeline(propertyStore, sharedAllDerivations)
|
|
6036
|
-
: computed(() => null);
|
|
6037
|
-
}
|
|
6038
|
-
setupValuePipeline(sharedAllDerivations) {
|
|
6039
|
-
this.formOptions = inject(FORM_OPTIONS);
|
|
6040
|
-
const collection = computed(() => {
|
|
6041
|
-
const fields = this.config.schemaFields();
|
|
6042
|
-
if (!fields || fields.length === 0) {
|
|
6043
|
-
return null;
|
|
6044
|
-
}
|
|
6045
|
-
const collected = sharedAllDerivations ? (sharedAllDerivations()?.value ?? null) : collectDerivations(fields);
|
|
6046
|
-
if (!collected || collected.entries.length === 0) {
|
|
6047
|
-
return null;
|
|
6048
|
-
}
|
|
6049
|
-
validateNoCycles(collected, this.logger);
|
|
6050
|
-
if (DEV_MODE) {
|
|
6051
|
-
warnAboutWildcardDependencies(this.logger, collected.entries, fields.length);
|
|
6052
|
-
warnAboutMisconfiguredReEngagement(this.logger, collected.entries);
|
|
6053
|
-
}
|
|
6054
|
-
return collected;
|
|
6055
|
-
}, ...(ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []));
|
|
6056
|
-
setupOnChangeStream({
|
|
6057
|
-
injector: this.injector,
|
|
6058
|
-
destroyRef: this.destroyRef,
|
|
6059
|
-
logger: this.logger,
|
|
6060
|
-
errorLabel: 'Derivation onChange stream error',
|
|
6061
|
-
collectionSignal: collection,
|
|
6062
|
-
formValueSignal: this.config.formValue,
|
|
6063
|
-
additionalAwakeners: [this.config.form],
|
|
6064
|
-
applyOnChange: (current, changedFields) => {
|
|
6065
|
-
this.applyOnChangeDerivations(current, untracked(() => this.config.form()), changedFields);
|
|
6066
|
-
},
|
|
6067
|
-
});
|
|
6068
|
-
setupDebouncedStream({
|
|
6069
|
-
injector: this.injector,
|
|
6070
|
-
destroyRef: this.destroyRef,
|
|
6071
|
-
logger: this.logger,
|
|
6072
|
-
errorLabel: 'Derivation debounced stream error',
|
|
6073
|
-
collectionSignal: collection,
|
|
6074
|
-
formValueSignal: this.config.formValue,
|
|
6075
|
-
applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
|
|
6076
|
-
const formAccessor = untracked(() => this.config.form());
|
|
6077
|
-
if (!formAccessor)
|
|
6078
|
-
return;
|
|
6079
|
-
this.applyDebouncedEntriesForPeriod(debounceMs, current, formAccessor, changedFields);
|
|
6080
|
-
},
|
|
6081
|
-
});
|
|
6082
|
-
this.setupValueHttpStreams(collection);
|
|
6083
|
-
this.setupValueAsyncFunctionStreams(collection);
|
|
6084
|
-
return collection;
|
|
6085
|
-
}
|
|
6086
|
-
setupPropertyPipeline(propertyStore, sharedAllDerivations) {
|
|
6087
|
-
const collection = computed(() => {
|
|
6088
|
-
const fields = this.config.schemaFields();
|
|
6089
|
-
if (!fields || fields.length === 0) {
|
|
6090
|
-
return null;
|
|
6091
|
-
}
|
|
6092
|
-
const collected = sharedAllDerivations ? (sharedAllDerivations()?.property ?? null) : collectPropertyDerivations(fields);
|
|
6093
|
-
if (!collected || collected.entries.length === 0) {
|
|
6094
|
-
return null;
|
|
6095
|
-
}
|
|
6096
|
-
return collected;
|
|
6097
|
-
}, ...(ngDevMode ? [{ debugName: "collection" }] : /* istanbul ignore next */ []));
|
|
6098
|
-
explicitEffect([collection, this.config.schemaFields], ([current, fields]) => {
|
|
6099
|
-
propertyStore.clear();
|
|
6100
|
-
if (!current)
|
|
6101
|
-
return;
|
|
6102
|
-
for (const entry of current.entries) {
|
|
6103
|
-
propertyStore.registerField(entry.fieldKey);
|
|
6104
|
-
}
|
|
6105
|
-
if (DEV_MODE) {
|
|
6106
|
-
warnAboutWildcardPropertyDependencies(this.logger, current.entries, fields?.length ?? 0);
|
|
6107
|
-
}
|
|
6108
|
-
});
|
|
6109
|
-
setupOnChangeStream({
|
|
6110
|
-
injector: this.injector,
|
|
6111
|
-
destroyRef: this.destroyRef,
|
|
6112
|
-
logger: this.logger,
|
|
6113
|
-
errorLabel: 'Property derivation onChange stream error',
|
|
6114
|
-
collectionSignal: collection,
|
|
6115
|
-
formValueSignal: this.config.formValue,
|
|
6116
|
-
applyOnChange: (current) => this.applyOnChangePropertyDerivations(current, propertyStore),
|
|
6117
|
-
});
|
|
6118
|
-
setupDebouncedStream({
|
|
6119
|
-
injector: this.injector,
|
|
6120
|
-
destroyRef: this.destroyRef,
|
|
6121
|
-
logger: this.logger,
|
|
6122
|
-
errorLabel: 'Property derivation debounced stream error',
|
|
6123
|
-
collectionSignal: collection,
|
|
6124
|
-
formValueSignal: this.config.formValue,
|
|
6125
|
-
applyDebouncedForPeriod: (debounceMs, current, changedFields) => {
|
|
6126
|
-
this.applyDebouncedPropertyEntriesForPeriod(debounceMs, current, propertyStore, changedFields);
|
|
6127
|
-
},
|
|
6128
|
-
});
|
|
6129
|
-
this.setupPropertyHttpStreams(collection, propertyStore);
|
|
6130
|
-
this.setupPropertyAsyncFunctionStreams(collection, propertyStore);
|
|
6131
|
-
return collection;
|
|
6132
|
-
}
|
|
6133
|
-
applyOnChangeDerivations(collection, formAccessor, changedFields) {
|
|
6134
|
-
const applicatorContext = {
|
|
6135
|
-
formValue: this.config.formValue,
|
|
6136
|
-
rootForm: formAccessor,
|
|
6137
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6138
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6139
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6140
|
-
logger: this.logger,
|
|
6141
|
-
warningTracker: this.warningTracker,
|
|
6142
|
-
derivationLogger: untracked(() => this.config.derivationLogger()),
|
|
6143
|
-
maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations),
|
|
6144
|
-
};
|
|
6145
|
-
const result = applyDerivationsForTrigger(collection, 'onChange', applicatorContext, changedFields);
|
|
6146
|
-
if (result.maxIterationsReached) {
|
|
6147
|
-
this.logger.warn(`Derivation processing reached max iterations. ` +
|
|
6148
|
-
`This may indicate a loop in derivation logic that wasn't caught at build time. ` +
|
|
6149
|
-
`Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
|
|
6150
|
-
}
|
|
6151
|
-
}
|
|
6152
|
-
applyDebouncedEntriesForPeriod(debounceMs, collection, formAccessor, changedFields) {
|
|
6153
|
-
const debouncedEntries = collection.entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === debounceMs);
|
|
6154
|
-
if (debouncedEntries.length === 0) {
|
|
6155
|
-
return;
|
|
6156
|
-
}
|
|
6157
|
-
const filteredCollection = {
|
|
6158
|
-
entries: debouncedEntries,
|
|
6159
|
-
};
|
|
6160
|
-
const applicatorContext = {
|
|
6161
|
-
formValue: this.config.formValue,
|
|
6162
|
-
rootForm: formAccessor,
|
|
6163
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6164
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6165
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6166
|
-
logger: this.logger,
|
|
6167
|
-
warningTracker: this.warningTracker,
|
|
6168
|
-
derivationLogger: untracked(() => this.config.derivationLogger()),
|
|
6169
|
-
maxIterations: untracked(() => this.formOptions()?.maxDerivationIterations),
|
|
6170
|
-
};
|
|
6171
|
-
const result = applyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
|
|
6172
|
-
if (result.maxIterationsReached) {
|
|
6173
|
-
this.logger.warn(`Debounced derivation processing reached max iterations (${debounceMs}ms). ` +
|
|
6174
|
-
`Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
|
|
6175
|
-
}
|
|
6176
|
-
}
|
|
6177
|
-
setupValueHttpStreams(collectionSignal) {
|
|
6178
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6179
|
-
setupEntryAsyncStream({
|
|
6180
|
-
injector: this.injector,
|
|
6181
|
-
destroyRef: this.destroyRef,
|
|
6182
|
-
logger: this.logger,
|
|
6183
|
-
errorLabel: 'HTTP Derivation: outer stream error',
|
|
6184
|
-
collectionSignal,
|
|
6185
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
|
|
6186
|
-
entrySignature: valueHttpEntrySignature,
|
|
6187
|
-
createStream: (entry) => {
|
|
6188
|
-
if (!this.httpClient) {
|
|
6189
|
-
this.logger.error('HTTP Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
|
|
6190
|
-
return null;
|
|
6191
|
-
}
|
|
6192
|
-
const context = {
|
|
6193
|
-
formValue: this.config.formValue,
|
|
6194
|
-
form: this.config.form,
|
|
6195
|
-
httpClient: this.httpClient,
|
|
6196
|
-
logger: this.logger,
|
|
6197
|
-
derivationLogger: this.config.derivationLogger,
|
|
6198
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6199
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6200
|
-
warningTracker: this.warningTracker,
|
|
6201
|
-
};
|
|
6202
|
-
return createHttpDerivationStream(entry, formValue$, context);
|
|
6203
|
-
},
|
|
6204
|
-
});
|
|
6205
|
-
}
|
|
6206
|
-
setupValueAsyncFunctionStreams(collectionSignal) {
|
|
6207
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6208
|
-
setupEntryAsyncStream({
|
|
6209
|
-
injector: this.injector,
|
|
6210
|
-
destroyRef: this.destroyRef,
|
|
6211
|
-
logger: this.logger,
|
|
6212
|
-
errorLabel: 'Async Derivation: outer stream error',
|
|
6213
|
-
collectionSignal,
|
|
6214
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
|
|
6215
|
-
entrySignature: valueAsyncEntrySignature,
|
|
6216
|
-
createStream: (entry) => createAsyncDerivationStream(entry, formValue$, {
|
|
6217
|
-
formValue: this.config.formValue,
|
|
6218
|
-
form: this.config.form,
|
|
6219
|
-
logger: this.logger,
|
|
6220
|
-
derivationLogger: this.config.derivationLogger,
|
|
6221
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6222
|
-
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
6223
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6224
|
-
warningTracker: this.warningTracker,
|
|
6225
|
-
}),
|
|
6226
|
-
});
|
|
6227
|
-
}
|
|
6228
|
-
applyOnChangePropertyDerivations(collection, store) {
|
|
6229
|
-
const applicatorContext = {
|
|
6230
|
-
formValue: this.config.formValue,
|
|
6231
|
-
store,
|
|
6232
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6233
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6234
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6235
|
-
logger: this.logger,
|
|
6236
|
-
warningTracker: this.warningTracker,
|
|
6237
|
-
};
|
|
6238
|
-
applyPropertyDerivationsForTrigger(collection, 'onChange', applicatorContext);
|
|
6239
|
-
}
|
|
6240
|
-
applyDebouncedPropertyEntriesForPeriod(debounceMs, collection, store, changedFields) {
|
|
6241
|
-
const debouncedEntries = collection.entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === debounceMs);
|
|
6242
|
-
if (debouncedEntries.length === 0)
|
|
6243
|
-
return;
|
|
6244
|
-
const filteredCollection = { entries: debouncedEntries };
|
|
6245
|
-
const applicatorContext = {
|
|
6246
|
-
formValue: this.config.formValue,
|
|
6247
|
-
store,
|
|
6248
|
-
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
6249
|
-
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
6250
|
-
externalData: resolveExternalData(this.config.externalData),
|
|
6251
|
-
logger: this.logger,
|
|
6252
|
-
warningTracker: this.warningTracker,
|
|
6253
|
-
};
|
|
6254
|
-
applyPropertyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
|
|
6255
|
-
}
|
|
6256
|
-
setupPropertyHttpStreams(collectionSignal, store) {
|
|
6257
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6258
|
-
setupEntryAsyncStream({
|
|
6259
|
-
injector: this.injector,
|
|
6260
|
-
destroyRef: this.destroyRef,
|
|
6261
|
-
logger: this.logger,
|
|
6262
|
-
errorLabel: 'HTTP Property Derivation: outer stream error',
|
|
6263
|
-
collectionSignal,
|
|
6264
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.http) ?? [],
|
|
6265
|
-
entrySignature: propertyHttpEntrySignature,
|
|
6266
|
-
createStream: (entry) => {
|
|
6267
|
-
if (!this.httpClient) {
|
|
6268
|
-
this.logger.error('HTTP Property Derivation: HttpClient is not available. Ensure provideHttpClient() is included in your application providers.');
|
|
6269
|
-
return null;
|
|
6270
|
-
}
|
|
6271
|
-
return createHttpPropertyDerivationStream(entry, formValue$, {
|
|
6272
|
-
formValue: this.config.formValue,
|
|
6273
|
-
store,
|
|
6274
|
-
httpClient: this.httpClient,
|
|
6275
|
-
logger: this.logger,
|
|
6276
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6277
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6278
|
-
});
|
|
6279
|
-
},
|
|
6280
|
-
});
|
|
6281
|
-
}
|
|
6282
|
-
setupPropertyAsyncFunctionStreams(collectionSignal, store) {
|
|
6283
|
-
const formValue$ = toObservable(this.config.formValue, { injector: this.injector });
|
|
6284
|
-
setupEntryAsyncStream({
|
|
6285
|
-
injector: this.injector,
|
|
6286
|
-
destroyRef: this.destroyRef,
|
|
6287
|
-
logger: this.logger,
|
|
6288
|
-
errorLabel: 'Async Property Derivation: outer stream error',
|
|
6289
|
-
collectionSignal,
|
|
6290
|
-
selectEntries: (collection) => collection?.entries.filter((entry) => entry.asyncFunctionName || entry.asyncFn) ?? [],
|
|
6291
|
-
entrySignature: propertyAsyncEntrySignature,
|
|
6292
|
-
createStream: (entry) => createAsyncPropertyDerivationStream(entry, formValue$, {
|
|
6293
|
-
formValue: this.config.formValue,
|
|
6294
|
-
store,
|
|
6295
|
-
logger: this.logger,
|
|
6296
|
-
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
6297
|
-
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
6298
|
-
externalData: () => resolveExternalData(this.config.externalData),
|
|
6299
|
-
}),
|
|
6300
|
-
});
|
|
6301
|
-
}
|
|
6302
|
-
}
|
|
6303
|
-
function valueHttpEntrySignature(entry) {
|
|
6304
|
-
const config = {
|
|
6305
|
-
http: entry.http,
|
|
6306
|
-
responseExpression: entry.responseExpression,
|
|
6307
|
-
dependsOn: entry.dependsOn,
|
|
6308
|
-
debounceMs: entry.debounceMs,
|
|
6309
|
-
trigger: entry.trigger,
|
|
6310
|
-
condition: entry.condition,
|
|
6311
|
-
stopOnUserOverride: entry.stopOnUserOverride,
|
|
6312
|
-
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
6313
|
-
};
|
|
6314
|
-
return `${entry.fieldKey}:${JSON.stringify(config)}`;
|
|
6315
|
-
}
|
|
6316
|
-
function valueAsyncEntrySignature(entry) {
|
|
6317
|
-
const config = {
|
|
6318
|
-
asyncFunctionName: entry.asyncFunctionName,
|
|
6319
|
-
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}` : undefined,
|
|
6320
|
-
dependsOn: entry.dependsOn,
|
|
6321
|
-
debounceMs: entry.debounceMs,
|
|
6322
|
-
trigger: entry.trigger,
|
|
6323
|
-
condition: entry.condition,
|
|
6324
|
-
stopOnUserOverride: entry.stopOnUserOverride,
|
|
6325
|
-
reEngageOnDependencyChange: entry.reEngageOnDependencyChange,
|
|
6326
|
-
};
|
|
6327
|
-
return `${entry.fieldKey}:${JSON.stringify(config)}`;
|
|
6328
|
-
}
|
|
6329
|
-
function propertyHttpEntrySignature(entry) {
|
|
6330
|
-
const config = {
|
|
6331
|
-
http: entry.http,
|
|
6332
|
-
responseExpression: entry.responseExpression,
|
|
6333
|
-
dependsOn: entry.dependsOn,
|
|
6334
|
-
debounceMs: entry.debounceMs,
|
|
6335
|
-
trigger: entry.trigger,
|
|
6336
|
-
condition: entry.condition,
|
|
6337
|
-
};
|
|
6338
|
-
return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
|
|
6339
|
-
}
|
|
6340
|
-
function propertyAsyncEntrySignature(entry) {
|
|
6341
|
-
const config = {
|
|
6342
|
-
asyncFunctionName: entry.asyncFunctionName,
|
|
6343
|
-
asyncFnId: entry.asyncFn ? `inline:${entry.fieldKey}.${entry.targetProperty}` : undefined,
|
|
6344
|
-
dependsOn: entry.dependsOn,
|
|
6345
|
-
debounceMs: entry.debounceMs,
|
|
6346
|
-
trigger: entry.trigger,
|
|
6347
|
-
condition: entry.condition,
|
|
6348
|
-
};
|
|
6349
|
-
return `${entry.fieldKey}.${entry.targetProperty}:${JSON.stringify(config)}`;
|
|
6350
|
-
}
|
|
6351
|
-
function createDerivationOrchestrator(config) {
|
|
6352
|
-
return new DerivationOrchestrator(config);
|
|
6353
|
-
}
|
|
6354
|
-
const DERIVATION_ORCHESTRATOR = new InjectionToken('DERIVATION_ORCHESTRATOR');
|
|
6355
|
-
const PROPERTY_DERIVATION_ORCHESTRATOR = new InjectionToken('PROPERTY_DERIVATION_ORCHESTRATOR');
|
|
6356
|
-
function injectDerivationOrchestrator() {
|
|
6357
|
-
return inject(DERIVATION_ORCHESTRATOR);
|
|
6358
|
-
}
|
|
6359
|
-
function warnAboutWildcardDependencies(logger, entries, fieldCount) {
|
|
6360
|
-
const wildcardEntries = entries.filter((entry) => entry.dependsOn.includes('*'));
|
|
6361
|
-
if (wildcardEntries.length === 0)
|
|
6362
|
-
return;
|
|
6363
|
-
const implicitWildcards = wildcardEntries.filter((entry) => !entry.http &&
|
|
6364
|
-
!entry.asyncFunctionName &&
|
|
6365
|
-
!entry.asyncFn &&
|
|
6366
|
-
(entry.functionName || entry.fn) &&
|
|
6367
|
-
(!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
|
|
6368
|
-
if (implicitWildcards.length > 0) {
|
|
6369
|
-
const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey} (${e.functionName ?? '<inline fn>'})`);
|
|
6370
|
-
logger.warn('Derivation: custom functions without explicit dependsOn detected. ' +
|
|
6371
|
-
`These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). ` +
|
|
6372
|
-
'Consider specifying explicit dependsOn arrays for better performance.', derivationDescs);
|
|
6373
|
-
}
|
|
6374
|
-
}
|
|
6375
|
-
function warnAboutMisconfiguredReEngagement(logger, entries) {
|
|
6376
|
-
const misconfigured = entries.filter((entry) => entry.reEngageOnDependencyChange && !entry.stopOnUserOverride);
|
|
6377
|
-
if (misconfigured.length === 0)
|
|
6378
|
-
return;
|
|
6379
|
-
const fieldKeys = misconfigured.map((e) => e.debugName ?? e.fieldKey);
|
|
6380
|
-
logger.warn('Derivation: reEngageOnDependencyChange without stopOnUserOverride detected. ' +
|
|
6381
|
-
'reEngageOnDependencyChange only takes effect when stopOnUserOverride is true. ' +
|
|
6382
|
-
'Either add stopOnUserOverride: true or remove reEngageOnDependencyChange.', fieldKeys);
|
|
6383
|
-
}
|
|
6384
|
-
function warnAboutWildcardPropertyDependencies(logger, entries, fieldCount) {
|
|
6385
|
-
const implicitWildcards = entries.filter((entry) => entry.dependsOn.includes('*') &&
|
|
6386
|
-
(entry.functionName || entry.fn) &&
|
|
6387
|
-
(!entry.originalConfig?.dependsOn || entry.originalConfig.dependsOn.length === 0));
|
|
6388
|
-
if (implicitWildcards.length > 0) {
|
|
6389
|
-
const derivationDescs = implicitWildcards.map((e) => `${e.fieldKey}.${e.targetProperty} (${e.functionName ?? '<inline fn>'})`);
|
|
6390
|
-
logger.warn('PropertyDerivation: custom functions without explicit dependsOn detected. ' +
|
|
6391
|
-
`These run on EVERY form change, which may impact performance (form has ${fieldCount} fields). ` +
|
|
6392
|
-
'Consider specifying explicit dependsOn arrays for better performance.', derivationDescs);
|
|
6393
|
-
}
|
|
6394
|
-
}
|
|
6395
|
-
|
|
6396
|
-
class ActiveDerivationLogger {
|
|
6397
|
-
logger;
|
|
6398
|
-
config = createDefaultDerivationLogConfig();
|
|
6399
|
-
setConfig(config) {
|
|
6400
|
-
this.config = config;
|
|
6401
|
-
}
|
|
6402
|
-
cycleStart(trigger, entryCount) {
|
|
6403
|
-
if (!shouldLog(this.config, 'verbose'))
|
|
6404
|
-
return;
|
|
6405
|
-
this.logger.debug(`Derivation - Starting cycle (${trigger}) with ${entryCount} derivation(s)`);
|
|
6406
|
-
}
|
|
6407
|
-
iteration(iterationNumber) {
|
|
6408
|
-
if (!shouldLog(this.config, 'verbose'))
|
|
6409
|
-
return;
|
|
6410
|
-
this.logger.debug(`Derivation - Iteration ${iterationNumber}`);
|
|
6411
|
-
}
|
|
6412
|
-
evaluation(entry) {
|
|
6413
|
-
if (!shouldLog(this.config, 'verbose'))
|
|
6414
|
-
return;
|
|
6415
|
-
const name = entry.debugName ? `"${entry.debugName}"` : entry.fieldKey;
|
|
6416
|
-
switch (entry.result) {
|
|
6417
|
-
case 'applied':
|
|
6418
|
-
this.logger.debug(`Derivation - Applied ${name}`, {
|
|
6419
|
-
field: entry.fieldKey,
|
|
6420
|
-
previousValue: entry.previousValue,
|
|
6421
|
-
newValue: entry.newValue,
|
|
6422
|
-
...(entry.durationMs !== undefined && { durationMs: entry.durationMs }),
|
|
6423
|
-
});
|
|
6424
|
-
break;
|
|
6425
|
-
case 'skipped':
|
|
6426
|
-
this.logger.debug(`Derivation - Skipped ${name} (${this.formatSkipReason(entry.skipReason)})`, {
|
|
6427
|
-
field: entry.fieldKey,
|
|
6428
|
-
reason: entry.skipReason,
|
|
6429
|
-
});
|
|
6430
|
-
break;
|
|
6431
|
-
case 'error':
|
|
6432
|
-
this.logger.debug(`Derivation - Error ${name}`, {
|
|
6433
|
-
field: entry.fieldKey,
|
|
6434
|
-
error: entry.error,
|
|
6435
|
-
});
|
|
6436
|
-
break;
|
|
6437
|
-
}
|
|
6438
|
-
}
|
|
6439
|
-
summary(result, trigger) {
|
|
6440
|
-
if (!shouldLog(this.config, 'summary'))
|
|
6441
|
-
return;
|
|
6442
|
-
if (result.appliedCount === 0 && result.errorCount === 0)
|
|
6443
|
-
return;
|
|
6444
|
-
this.logger.debug(`Derivation - Cycle complete (${trigger})`, {
|
|
6445
|
-
applied: result.appliedCount,
|
|
6446
|
-
skipped: result.skippedCount,
|
|
6447
|
-
errors: result.errorCount,
|
|
6448
|
-
iterations: result.iterations,
|
|
6449
|
-
});
|
|
6450
|
-
}
|
|
6451
|
-
maxIterationsReached(result, trigger) {
|
|
6452
|
-
this.logger.warn(`Derivation - Max iterations reached (${trigger}). ` +
|
|
6453
|
-
`This may indicate a loop in derivation logic. ` +
|
|
6454
|
-
`Applied: ${result.appliedCount}, Skipped: ${result.skippedCount}, Errors: ${result.errorCount}`);
|
|
6455
|
-
}
|
|
6456
|
-
formatSkipReason(reason) {
|
|
6457
|
-
switch (reason) {
|
|
6458
|
-
case 'condition-false':
|
|
6459
|
-
return 'condition not met';
|
|
6460
|
-
case 'value-unchanged':
|
|
6461
|
-
return 'value unchanged';
|
|
6462
|
-
case 'already-applied':
|
|
6463
|
-
return 'already applied this cycle';
|
|
6464
|
-
case 'user-override':
|
|
6465
|
-
return 'user override active';
|
|
6466
|
-
case 'target-not-found':
|
|
6467
|
-
return 'target not found';
|
|
6468
|
-
default:
|
|
6469
|
-
return 'unknown';
|
|
6470
|
-
}
|
|
6471
|
-
}
|
|
6472
|
-
}
|
|
6473
|
-
class NoopDerivationLogger {
|
|
6474
|
-
cycleStart() {
|
|
6475
|
-
/* no-op */
|
|
6476
|
-
}
|
|
6477
|
-
iteration() {
|
|
6478
|
-
/* no-op */
|
|
6479
|
-
}
|
|
6480
|
-
evaluation() {
|
|
6481
|
-
/* no-op */
|
|
6482
|
-
}
|
|
6483
|
-
summary() {
|
|
6484
|
-
/* no-op */
|
|
6485
|
-
}
|
|
6486
|
-
maxIterationsReached() {
|
|
6487
|
-
/* no-op */
|
|
6488
|
-
}
|
|
6489
|
-
}
|
|
6490
|
-
const NOOP_INSTANCE = new NoopDerivationLogger();
|
|
6491
|
-
function createDerivationLogger(config, logger) {
|
|
6492
|
-
const effectiveConfig = config ?? createDefaultDerivationLogConfig();
|
|
6493
|
-
if (effectiveConfig.level === 'none') {
|
|
6494
|
-
return NOOP_INSTANCE;
|
|
6495
|
-
}
|
|
6496
|
-
const service = new ActiveDerivationLogger();
|
|
6497
|
-
service.logger = logger;
|
|
6498
|
-
service.setConfig(effectiveConfig);
|
|
6499
|
-
return service;
|
|
6500
|
-
}
|
|
6501
|
-
|
|
6502
|
-
function isDynamicFormFeature(value) {
|
|
6503
|
-
return (typeof value === 'object' &&
|
|
6504
|
-
value !== null &&
|
|
6505
|
-
'ɵkind' in value &&
|
|
6506
|
-
typeof value.ɵkind === 'string' &&
|
|
6507
|
-
'ɵproviders' in value &&
|
|
6508
|
-
Array.isArray(value.ɵproviders));
|
|
6509
|
-
}
|
|
6510
|
-
function createFeature(kind, providers) {
|
|
6511
|
-
return {
|
|
6512
|
-
ɵkind: kind,
|
|
6513
|
-
ɵproviders: providers,
|
|
6514
|
-
};
|
|
6515
|
-
}
|
|
6516
|
-
|
|
6517
|
-
class ConsoleLogger {
|
|
6518
|
-
prefix = '[Dynamic Forms]';
|
|
6519
|
-
debug(message, ...args) {
|
|
6520
|
-
console.debug(this.prefix, message, ...args);
|
|
6521
|
-
}
|
|
6522
|
-
info(message, ...args) {
|
|
6523
|
-
console.info(this.prefix, message, ...args);
|
|
6524
|
-
}
|
|
6525
|
-
warn(message, ...args) {
|
|
6526
|
-
console.warn(this.prefix, message, ...args);
|
|
6527
|
-
}
|
|
6528
|
-
error(message, ...args) {
|
|
6529
|
-
console.error(this.prefix, message, ...args);
|
|
6530
|
-
}
|
|
6531
|
-
}
|
|
6532
|
-
|
|
6533
|
-
const DERIVATION_LOG_CONFIG = new InjectionToken('DERIVATION_LOG_CONFIG', {
|
|
6534
|
-
providedIn: 'root',
|
|
6535
|
-
factory: createDefaultDerivationLogConfig,
|
|
6536
|
-
});
|
|
6537
|
-
function withLoggerConfig(config = true) {
|
|
6538
|
-
if (typeof config === 'boolean' || typeof config === 'function') {
|
|
6539
|
-
const isEnabled = typeof config === 'function' ? config() : config;
|
|
6540
|
-
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
6541
|
-
return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
|
|
6542
|
-
}
|
|
6543
|
-
const isEnabled = config.enabled !== false;
|
|
6544
|
-
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
6545
|
-
const providers = [{ provide: DynamicFormLogger, useValue: logger }];
|
|
6546
|
-
if (config.derivations !== undefined) {
|
|
6547
|
-
providers.push({
|
|
6548
|
-
provide: DERIVATION_LOG_CONFIG,
|
|
6549
|
-
useValue: { level: config.derivations },
|
|
6550
|
-
});
|
|
6551
|
-
}
|
|
6552
|
-
return createFeature('logger', providers);
|
|
6553
|
-
}
|
|
6554
|
-
|
|
6555
|
-
const ADDON_ACTION_HANDLERS = new InjectionToken('ADDON_ACTION_HANDLERS');
|
|
6556
|
-
const ADDON_ACTION_REGISTRY = new InjectionToken('ADDON_ACTION_REGISTRY');
|
|
6557
|
-
function createAddonActionRegistry() {
|
|
6558
|
-
const sources = inject(ADDON_ACTION_HANDLERS, { optional: true }) ?? [];
|
|
6559
|
-
const map = new Map();
|
|
6560
|
-
for (const handlers of sources) {
|
|
6561
|
-
for (const [name, fn] of Object.entries(handlers)) {
|
|
6562
|
-
map.set(name, fn);
|
|
6563
|
-
}
|
|
6564
|
-
}
|
|
6565
|
-
return map;
|
|
6566
|
-
}
|
|
6567
|
-
|
|
6568
|
-
function coreProviders() {
|
|
6569
|
-
return [
|
|
6570
|
-
EventBus,
|
|
6571
|
-
{ provide: CONTAINER_FIELD_PROCESSORS, useFactory: createContainerFieldProcessors },
|
|
6572
|
-
SchemaRegistryService,
|
|
6573
|
-
FunctionRegistryService,
|
|
6574
|
-
{
|
|
6575
|
-
provide: FORM_STATE_DEPS,
|
|
6576
|
-
useFactory: () => ({ config: null, formOptions: null, value: null, source: null }),
|
|
6577
|
-
},
|
|
6578
|
-
FormStateManager,
|
|
6579
|
-
{
|
|
6580
|
-
provide: RootFormRegistryService,
|
|
6581
|
-
useFactory: (stateManager) => new RootFormRegistryService(stateManager.formValue, computed(() => stateManager.form())),
|
|
6582
|
-
deps: [FormStateManager],
|
|
6583
|
-
},
|
|
6584
|
-
FieldContextRegistryService,
|
|
6585
|
-
ArrayItemRegistryService,
|
|
6586
|
-
{
|
|
6587
|
-
provide: DEFAULT_PROPS,
|
|
6588
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultProps),
|
|
6589
|
-
deps: [FormStateManager],
|
|
6590
|
-
},
|
|
6591
|
-
{
|
|
6592
|
-
provide: DEFAULT_WRAPPERS,
|
|
6593
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultWrappers),
|
|
6594
|
-
deps: [FormStateManager],
|
|
6595
|
-
},
|
|
6596
|
-
{
|
|
6597
|
-
provide: DEFAULT_VALIDATION_MESSAGES,
|
|
6598
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.defaultValidationMessages),
|
|
6599
|
-
deps: [FormStateManager],
|
|
6600
|
-
},
|
|
6601
|
-
{
|
|
6602
|
-
provide: FORM_OPTIONS,
|
|
6603
|
-
useFactory: (stateManager) => stateManager.effectiveFormOptions,
|
|
6604
|
-
deps: [FormStateManager],
|
|
6605
|
-
},
|
|
6606
|
-
{
|
|
6607
|
-
provide: EXTERNAL_DATA,
|
|
6608
|
-
useFactory: (stateManager) => computed(() => stateManager.activeConfig()?.externalData),
|
|
6609
|
-
deps: [FormStateManager],
|
|
6610
|
-
},
|
|
6611
|
-
FormIdPrefixService,
|
|
6612
|
-
{
|
|
6613
|
-
provide: FORM_ID_PREFIX,
|
|
6614
|
-
useFactory: (svc) => svc.prefix,
|
|
6615
|
-
deps: [FormIdPrefixService],
|
|
6616
|
-
},
|
|
6617
|
-
{ provide: DEPRECATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
6618
|
-
{ provide: ADDON_ACTION_REGISTRY, useFactory: createAddonActionRegistry },
|
|
6619
|
-
];
|
|
6620
|
-
}
|
|
6621
|
-
function derivationProviders() {
|
|
6622
|
-
return [
|
|
6623
|
-
{ provide: DERIVATION_WARNING_TRACKER, useFactory: createWarningTracker },
|
|
6624
|
-
{
|
|
6625
|
-
provide: DERIVATION_ORCHESTRATOR,
|
|
6626
|
-
useFactory: (stateManager, logger, logConfig, externalData, propertyStore) => {
|
|
6627
|
-
const config = {
|
|
6628
|
-
schemaFields: computed(() => stateManager.formSetup()?.schemaFields),
|
|
6629
|
-
formValue: stateManager.formValue,
|
|
6630
|
-
form: computed(() => stateManager.form()),
|
|
6631
|
-
derivationLogger: computed(() => createDerivationLogger(logConfig, logger)),
|
|
6632
|
-
propertyStore,
|
|
6633
|
-
externalData,
|
|
6634
|
-
};
|
|
6635
|
-
return createDerivationOrchestrator(config);
|
|
6636
|
-
},
|
|
6637
|
-
deps: [FormStateManager, DynamicFormLogger, DERIVATION_LOG_CONFIG, EXTERNAL_DATA, PROPERTY_OVERRIDE_STORE],
|
|
6638
|
-
},
|
|
6639
|
-
{ provide: FORM_INITIALIZER, useExisting: DERIVATION_ORCHESTRATOR, multi: true },
|
|
6640
|
-
];
|
|
6641
|
-
}
|
|
6642
|
-
function propertyDerivationProviders() {
|
|
6643
|
-
return [
|
|
6644
|
-
{ provide: PROPERTY_OVERRIDE_STORE, useFactory: createPropertyOverrideStore },
|
|
6645
|
-
{ provide: PROPERTY_DERIVATION_ORCHESTRATOR, useExisting: DERIVATION_ORCHESTRATOR },
|
|
6646
|
-
];
|
|
6647
|
-
}
|
|
6648
|
-
function httpExpressionProviders() {
|
|
6649
|
-
return [{ provide: HTTP_CONDITION_CACHE, useFactory: () => new HttpConditionCache(100) }, HttpConditionFunctionCacheService];
|
|
6650
|
-
}
|
|
6651
|
-
function asyncExpressionProviders() {
|
|
6652
|
-
return [AsyncConditionFunctionCacheService];
|
|
6653
|
-
}
|
|
6654
|
-
function dynamicValueProviders() {
|
|
6655
|
-
return [DynamicValueFunctionCacheService];
|
|
6656
|
-
}
|
|
6657
|
-
function logicProviders() {
|
|
6658
|
-
return [LogicFunctionCacheService];
|
|
6659
|
-
}
|
|
6660
|
-
function provideDynamicFormDI() {
|
|
6661
|
-
return [
|
|
6662
|
-
...coreProviders(),
|
|
6663
|
-
...logicProviders(),
|
|
6664
|
-
...dynamicValueProviders(),
|
|
6665
|
-
...httpExpressionProviders(),
|
|
6666
|
-
...asyncExpressionProviders(),
|
|
6667
|
-
...propertyDerivationProviders(),
|
|
6668
|
-
...derivationProviders(),
|
|
6669
|
-
];
|
|
6670
|
-
}
|
|
6671
|
-
|
|
6672
|
-
class EventDispatcher {
|
|
6673
|
-
bus;
|
|
6674
|
-
dispatch(event) {
|
|
6675
|
-
this.bus?.emitInstance(event);
|
|
6676
|
-
}
|
|
6677
|
-
connect(bus) {
|
|
6678
|
-
this.bus = bus;
|
|
6679
|
-
}
|
|
6680
|
-
disconnect() {
|
|
6681
|
-
this.bus = undefined;
|
|
6682
|
-
}
|
|
6683
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6684
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher });
|
|
6685
|
-
}
|
|
6686
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EventDispatcher, decorators: [{
|
|
6687
|
-
type: Injectable
|
|
6688
|
-
}] });
|
|
6689
|
-
|
|
6690
|
-
class DfTemplate {
|
|
6691
|
-
dfTemplate = input.required(...(ngDevMode ? [{ debugName: "dfTemplate" }] : /* istanbul ignore next */ []));
|
|
6692
|
-
templateRef = inject(TemplateRef);
|
|
6693
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
6694
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: DfTemplate, isStandalone: true, selector: "ng-template[dfTemplate]", inputs: { dfTemplate: { classPropertyName: "dfTemplate", publicName: "dfTemplate", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["dfTemplate"], ngImport: i0 });
|
|
6695
|
-
}
|
|
6696
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfTemplate, decorators: [{
|
|
6697
|
-
type: Directive,
|
|
6698
|
-
args: [{
|
|
6699
|
-
selector: 'ng-template[dfTemplate]',
|
|
6700
|
-
exportAs: 'dfTemplate',
|
|
6701
|
-
}]
|
|
6702
|
-
}], propDecorators: { dfTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfTemplate", required: true }] }] } });
|
|
6703
|
-
class DfFieldTemplateRegistry {
|
|
6704
|
-
_map = signal(new Map(), ...(ngDevMode ? [{ debugName: "_map" }] : /* istanbul ignore next */ []));
|
|
6705
|
-
map = this._map.asReadonly();
|
|
6706
|
-
set(value) {
|
|
6707
|
-
this._map.set(value);
|
|
6708
|
-
}
|
|
6709
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6710
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry });
|
|
6711
|
-
}
|
|
6712
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfFieldTemplateRegistry, decorators: [{
|
|
6713
|
-
type: Injectable
|
|
6714
|
-
}] });
|
|
4419
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DfFieldTemplateRegistry, decorators: [{
|
|
4420
|
+
type: Injectable
|
|
4421
|
+
}] });
|
|
6715
4422
|
|
|
6716
4423
|
class DynamicForm {
|
|
6717
4424
|
config = input.required({ ...(ngDevMode ? { debugName: "config" } : /* istanbul ignore next */ {}), alias: 'dynamic-form' });
|
|
6718
|
-
formOptions = input(undefined,
|
|
6719
|
-
|
|
6720
|
-
|
|
4425
|
+
formOptions = input(undefined, /* @ts-ignore */
|
|
4426
|
+
...(ngDevMode ? [{ debugName: "formOptions" }] : /* istanbul ignore next */ []));
|
|
4427
|
+
value = model(undefined, /* @ts-ignore */
|
|
4428
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4429
|
+
source = input('inline', /* @ts-ignore */
|
|
4430
|
+
...(ngDevMode ? [{ debugName: "source" }] : /* istanbul ignore next */ []));
|
|
6721
4431
|
destroyRef = inject(DestroyRef);
|
|
6722
4432
|
injector = inject(Injector);
|
|
6723
4433
|
environmentInjector = inject(EnvironmentInjector);
|
|
@@ -6725,7 +4435,8 @@ class DynamicForm {
|
|
|
6725
4435
|
logger = inject(DynamicFormLogger);
|
|
6726
4436
|
dispatcher = inject(EventDispatcher, { optional: true });
|
|
6727
4437
|
templateRegistry = inject(DfFieldTemplateRegistry);
|
|
6728
|
-
_projectedTemplates = contentChildren(DfTemplate,
|
|
4438
|
+
_projectedTemplates = contentChildren(DfTemplate, /* @ts-ignore */
|
|
4439
|
+
...(ngDevMode ? [{ debugName: "_projectedTemplates" }] : /* istanbul ignore next */ []));
|
|
6729
4440
|
stateManager = this.connectDeps();
|
|
6730
4441
|
formIdPrefix = inject(FormIdPrefixService);
|
|
6731
4442
|
idPrefix = this.formIdPrefix.prefix;
|
|
@@ -6747,12 +4458,15 @@ class DynamicForm {
|
|
|
6747
4458
|
disabled = this.stateManager.disabled;
|
|
6748
4459
|
submitting = this.stateManager.submitting;
|
|
6749
4460
|
fieldLoadingErrors = this.stateManager.fieldLoadingErrors;
|
|
6750
|
-
|
|
4461
|
+
derivationReady = inject(DERIVATION_RENDER_GATE);
|
|
4462
|
+
shouldRender = computed(() => this.stateManager.shouldRender() && this.derivationReady(), /* @ts-ignore */
|
|
4463
|
+
...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
6751
4464
|
resolvedFields = this.stateManager.resolvedFields;
|
|
6752
4465
|
totalComponentsCount = computed(() => {
|
|
6753
4466
|
const fields = this.stateManager.formSetup()?.fields ?? [];
|
|
6754
4467
|
return countContainersRecursive(fields) + 1;
|
|
6755
|
-
},
|
|
4468
|
+
}, /* @ts-ignore */
|
|
4469
|
+
...(ngDevMode ? [{ debugName: "totalComponentsCount" }] : /* istanbul ignore next */ []));
|
|
6756
4470
|
initialized$ = setupInitializationTracking({
|
|
6757
4471
|
eventBus: this.eventBus,
|
|
6758
4472
|
totalComponentsCount: this.totalComponentsCount,
|
|
@@ -6777,7 +4491,7 @@ class DynamicForm {
|
|
|
6777
4491
|
}
|
|
6778
4492
|
onNativeSubmit(event) {
|
|
6779
4493
|
event.preventDefault();
|
|
6780
|
-
this.eventBus.dispatch(
|
|
4494
|
+
this.eventBus.dispatch(FormSubmitEvent);
|
|
6781
4495
|
}
|
|
6782
4496
|
setupEffects() {
|
|
6783
4497
|
explicitEffect([this.formModeDetection, this.pageFieldDefinitions], ([{ mode }, pages]) => {
|
|
@@ -6821,8 +4535,8 @@ class DynamicForm {
|
|
|
6821
4535
|
error: (err) => this.logger.error('Submission handler error', err),
|
|
6822
4536
|
});
|
|
6823
4537
|
}
|
|
6824
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
6825
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
4538
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4539
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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", 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: [
|
|
6826
4540
|
provideDynamicFormDI(),
|
|
6827
4541
|
DfFieldTemplateRegistry,
|
|
6828
4542
|
{ provide: DF_FIELD_TEMPLATES, useFactory: () => inject(DfFieldTemplateRegistry).map },
|
|
@@ -6850,7 +4564,7 @@ class DynamicForm {
|
|
|
6850
4564
|
}
|
|
6851
4565
|
`, 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 });
|
|
6852
4566
|
}
|
|
6853
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4567
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: DynamicForm, decorators: [{
|
|
6854
4568
|
type: Component,
|
|
6855
4569
|
args: [{ selector: 'form[dynamic-form]', imports: [DfFieldOutlet, PageOrchestratorComponent], template: `
|
|
6856
4570
|
@if (shouldRender()) {
|
|
@@ -6911,87 +4625,38 @@ function countContainersRecursive(fields) {
|
|
|
6911
4625
|
return count;
|
|
6912
4626
|
}
|
|
6913
4627
|
|
|
6914
|
-
const
|
|
6915
|
-
function injectAddonKindRegistry() {
|
|
6916
|
-
const registry = inject(ADDON_KIND_REGISTRY);
|
|
6917
|
-
const componentCache = inject(ADDON_KIND_COMPONENT_CACHE);
|
|
6918
|
-
return {
|
|
6919
|
-
getKind(kind) {
|
|
6920
|
-
return registry.get(kind);
|
|
6921
|
-
},
|
|
6922
|
-
hasKind(kind) {
|
|
6923
|
-
return registry.has(kind);
|
|
6924
|
-
},
|
|
6925
|
-
getKindNames() {
|
|
6926
|
-
return Array.from(registry.keys());
|
|
6927
|
-
},
|
|
6928
|
-
async loadKindComponent(kind) {
|
|
6929
|
-
const definition = registry.get(kind);
|
|
6930
|
-
if (!definition) {
|
|
6931
|
-
const known = Array.from(registry.keys()).join(', ') || '(none)';
|
|
6932
|
-
throw new DynamicFormError(`Addon kind '${kind}' is not registered. Currently registered kinds: ${known}.`);
|
|
6933
|
-
}
|
|
6934
|
-
const cached = componentCache.get(kind);
|
|
6935
|
-
if (cached) {
|
|
6936
|
-
return cached;
|
|
6937
|
-
}
|
|
6938
|
-
try {
|
|
6939
|
-
const component = resolveDefaultExport(await definition.loadComponent());
|
|
6940
|
-
if (component) {
|
|
6941
|
-
componentCache.set(kind, component);
|
|
6942
|
-
return component;
|
|
6943
|
-
}
|
|
6944
|
-
throw new DynamicFormError(`Addon kind '${kind}' loadComponent resolved to null.`);
|
|
6945
|
-
}
|
|
6946
|
-
catch (error) {
|
|
6947
|
-
if (error instanceof DynamicFormError)
|
|
6948
|
-
throw error;
|
|
6949
|
-
throw new DynamicFormError(`Failed to load component for addon kind '${kind}': ${error instanceof Error ? error.message : String(error)}`);
|
|
6950
|
-
}
|
|
6951
|
-
},
|
|
6952
|
-
getLoadedKindComponent(kind) {
|
|
6953
|
-
if (!registry.has(kind))
|
|
6954
|
-
return undefined;
|
|
6955
|
-
return componentCache.get(kind);
|
|
6956
|
-
},
|
|
6957
|
-
get raw() {
|
|
6958
|
-
return registry;
|
|
6959
|
-
},
|
|
6960
|
-
};
|
|
6961
|
-
}
|
|
6962
|
-
|
|
6963
|
-
const BUILT_IN_ADDON_KINDS = [
|
|
4628
|
+
const BUILT_IN_ADDON_TYPES = [
|
|
6964
4629
|
{
|
|
6965
|
-
|
|
4630
|
+
type: 'text',
|
|
6966
4631
|
loadComponent: () => Promise.resolve().then(function () { return textAddon_component; }).then((m) => m.TextAddonComponent),
|
|
6967
4632
|
validate: (addon, fieldKey) => {
|
|
6968
4633
|
const text = addon.text;
|
|
6969
4634
|
if (text === undefined || text === null) {
|
|
6970
|
-
throw new DynamicFormError(`Addon
|
|
4635
|
+
throw new DynamicFormError(`Addon type 'text' requires a 'text' field (field: '${fieldKey}').`);
|
|
6971
4636
|
}
|
|
6972
4637
|
if (typeof text === 'string' && text.length === 0) {
|
|
6973
|
-
throw new DynamicFormError(`Addon
|
|
4638
|
+
throw new DynamicFormError(`Addon type 'text' requires a non-empty 'text' string (field: '${fieldKey}').`);
|
|
6974
4639
|
}
|
|
6975
4640
|
},
|
|
6976
4641
|
},
|
|
6977
4642
|
{
|
|
6978
|
-
|
|
4643
|
+
type: 'template',
|
|
6979
4644
|
loadComponent: () => Promise.resolve().then(function () { return templateAddon_component; }).then((m) => m.TemplateAddonComponent),
|
|
6980
4645
|
validate: (addon, fieldKey) => {
|
|
6981
4646
|
const key = addon.templateKey;
|
|
6982
4647
|
if (typeof key !== 'string' || key.length === 0) {
|
|
6983
|
-
throw new DynamicFormError(`Addon
|
|
4648
|
+
throw new DynamicFormError(`Addon type 'template' requires a non-empty 'templateKey' string (field: '${fieldKey}').`);
|
|
6984
4649
|
}
|
|
6985
4650
|
},
|
|
6986
4651
|
},
|
|
6987
4652
|
{
|
|
6988
|
-
|
|
4653
|
+
type: 'component',
|
|
6989
4654
|
jsonSafe: false,
|
|
6990
4655
|
loadComponent: () => Promise.resolve().then(function () { return componentAddon_component; }).then((m) => m.ComponentAddonComponent),
|
|
6991
4656
|
validate: (addon, fieldKey) => {
|
|
6992
4657
|
const loader = addon.component;
|
|
6993
4658
|
if (typeof loader !== 'function') {
|
|
6994
|
-
throw new DynamicFormError(`Addon
|
|
4659
|
+
throw new DynamicFormError(`Addon type 'component' requires a 'component' loader function (field: '${fieldKey}').`);
|
|
6995
4660
|
}
|
|
6996
4661
|
},
|
|
6997
4662
|
},
|
|
@@ -7006,35 +4671,35 @@ const DISPLAY_FIELD_TYPES_BASE = {
|
|
|
7006
4671
|
const BUILT_IN_FIELDS = [
|
|
7007
4672
|
{
|
|
7008
4673
|
name: 'row',
|
|
7009
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
4674
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs'),
|
|
7010
4675
|
mapper: rowFieldMapper,
|
|
7011
4676
|
valueHandling: 'flatten',
|
|
7012
4677
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7013
4678
|
},
|
|
7014
4679
|
{
|
|
7015
4680
|
name: 'group',
|
|
7016
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-
|
|
4681
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-Ca4AqdZu.mjs'),
|
|
7017
4682
|
mapper: groupFieldMapper,
|
|
7018
4683
|
valueHandling: 'include',
|
|
7019
4684
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7020
4685
|
},
|
|
7021
4686
|
{
|
|
7022
4687
|
name: 'array',
|
|
7023
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-
|
|
4688
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-8rodySeE.mjs'),
|
|
7024
4689
|
mapper: arrayFieldMapper,
|
|
7025
4690
|
valueHandling: 'include',
|
|
7026
4691
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7027
4692
|
},
|
|
7028
4693
|
{
|
|
7029
4694
|
name: 'page',
|
|
7030
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-
|
|
4695
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-IsAtMNjD.mjs'),
|
|
7031
4696
|
mapper: pageFieldMapper,
|
|
7032
4697
|
valueHandling: 'flatten',
|
|
7033
4698
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
7034
4699
|
},
|
|
7035
4700
|
{
|
|
7036
4701
|
name: 'text',
|
|
7037
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-
|
|
4702
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-CNCpmf5_.mjs'),
|
|
7038
4703
|
mapper: textFieldMapper,
|
|
7039
4704
|
valueHandling: 'exclude',
|
|
7040
4705
|
...DISPLAY_FIELD_TYPES_BASE,
|
|
@@ -7045,7 +4710,7 @@ const BUILT_IN_FIELDS = [
|
|
|
7045
4710
|
},
|
|
7046
4711
|
{
|
|
7047
4712
|
name: 'container',
|
|
7048
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
4713
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-DFgjWEZN.mjs'),
|
|
7049
4714
|
mapper: containerFieldMapper,
|
|
7050
4715
|
valueHandling: 'flatten',
|
|
7051
4716
|
...CONTAINER_FIELD_TYPES_BASE,
|
|
@@ -7054,15 +4719,44 @@ const BUILT_IN_FIELDS = [
|
|
|
7054
4719
|
const BUILT_IN_WRAPPERS = [
|
|
7055
4720
|
{
|
|
7056
4721
|
wrapperName: 'css',
|
|
7057
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-
|
|
4722
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-AScQAJTQ.mjs'),
|
|
7058
4723
|
},
|
|
7059
4724
|
{
|
|
7060
4725
|
wrapperName: 'row',
|
|
7061
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-
|
|
4726
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-row-wrapper.component-BlVa801t.mjs'),
|
|
7062
4727
|
},
|
|
7063
4728
|
];
|
|
7064
4729
|
|
|
7065
|
-
|
|
4730
|
+
function isDynamicFormFeature(value) {
|
|
4731
|
+
return (typeof value === 'object' &&
|
|
4732
|
+
value !== null &&
|
|
4733
|
+
'ɵkind' in value &&
|
|
4734
|
+
typeof value.ɵkind === 'string' &&
|
|
4735
|
+
'ɵproviders' in value &&
|
|
4736
|
+
Array.isArray(value.ɵproviders));
|
|
4737
|
+
}
|
|
4738
|
+
function createFeature(kind, providers) {
|
|
4739
|
+
return {
|
|
4740
|
+
ɵkind: kind,
|
|
4741
|
+
ɵproviders: providers,
|
|
4742
|
+
};
|
|
4743
|
+
}
|
|
4744
|
+
|
|
4745
|
+
class ConsoleLogger {
|
|
4746
|
+
prefix = '[Dynamic Forms]';
|
|
4747
|
+
debug(message, ...args) {
|
|
4748
|
+
console.debug(this.prefix, message, ...args);
|
|
4749
|
+
}
|
|
4750
|
+
info(message, ...args) {
|
|
4751
|
+
console.info(this.prefix, message, ...args);
|
|
4752
|
+
}
|
|
4753
|
+
warn(message, ...args) {
|
|
4754
|
+
console.warn(this.prefix, message, ...args);
|
|
4755
|
+
}
|
|
4756
|
+
error(message, ...args) {
|
|
4757
|
+
console.error(this.prefix, message, ...args);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
7066
4760
|
|
|
7067
4761
|
function createWrappers(...registrations) {
|
|
7068
4762
|
return {
|
|
@@ -7144,28 +4838,50 @@ function provideDynamicForm(...items) {
|
|
|
7144
4838
|
},
|
|
7145
4839
|
},
|
|
7146
4840
|
{
|
|
7147
|
-
provide:
|
|
4841
|
+
provide: ADDON_TYPE_REGISTRY,
|
|
7148
4842
|
useFactory: () => {
|
|
7149
4843
|
const logger = inject(DynamicFormLogger);
|
|
7150
|
-
const contributed = inject(
|
|
4844
|
+
const contributed = inject(ADDON_TYPE_DEFINITIONS, { optional: true }) ?? [];
|
|
7151
4845
|
const registry = new Map();
|
|
7152
|
-
for (const def of
|
|
7153
|
-
registry.set(def.
|
|
4846
|
+
for (const def of BUILT_IN_ADDON_TYPES) {
|
|
4847
|
+
registry.set(def.type, def);
|
|
7154
4848
|
}
|
|
7155
4849
|
for (const def of contributed) {
|
|
7156
|
-
if (registry.has(def.
|
|
7157
|
-
logger.warn(`Addon
|
|
4850
|
+
if (registry.has(def.type)) {
|
|
4851
|
+
logger.warn(`Addon type "${def.type}" is already registered. Overwriting.`);
|
|
7158
4852
|
}
|
|
7159
|
-
registry.set(def.
|
|
4853
|
+
registry.set(def.type, def);
|
|
7160
4854
|
}
|
|
7161
4855
|
return registry;
|
|
7162
4856
|
},
|
|
7163
4857
|
},
|
|
7164
|
-
{ provide:
|
|
4858
|
+
{ provide: ADDON_TYPE_COMPONENT_CACHE, useFactory: () => new Map() },
|
|
7165
4859
|
...featureProviders,
|
|
7166
4860
|
]);
|
|
7167
4861
|
}
|
|
7168
4862
|
|
|
4863
|
+
const DERIVATION_LOG_CONFIG = new InjectionToken('DERIVATION_LOG_CONFIG', {
|
|
4864
|
+
providedIn: 'root',
|
|
4865
|
+
factory: createDefaultDerivationLogConfig,
|
|
4866
|
+
});
|
|
4867
|
+
function withLoggerConfig(config = true) {
|
|
4868
|
+
if (typeof config === 'boolean' || typeof config === 'function') {
|
|
4869
|
+
const isEnabled = typeof config === 'function' ? config() : config;
|
|
4870
|
+
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
4871
|
+
return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
|
|
4872
|
+
}
|
|
4873
|
+
const isEnabled = config.enabled !== false;
|
|
4874
|
+
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
4875
|
+
const providers = [{ provide: DynamicFormLogger, useValue: logger }];
|
|
4876
|
+
if (config.derivations !== undefined) {
|
|
4877
|
+
providers.push({
|
|
4878
|
+
provide: DERIVATION_LOG_CONFIG,
|
|
4879
|
+
useValue: { level: config.derivations },
|
|
4880
|
+
});
|
|
4881
|
+
}
|
|
4882
|
+
return createFeature('logger', providers);
|
|
4883
|
+
}
|
|
4884
|
+
|
|
7169
4885
|
function withEventFormValue() {
|
|
7170
4886
|
return createFeature('event-form-value', [{ provide: EMIT_FORM_VALUE_ON_EVENTS, useValue: true }]);
|
|
7171
4887
|
}
|
|
@@ -7194,94 +4910,15 @@ function wrapperProps() {
|
|
|
7194
4910
|
return undefined;
|
|
7195
4911
|
}
|
|
7196
4912
|
|
|
7197
|
-
class DfAddonSlot {
|
|
7198
|
-
registry = injectAddonKindRegistry();
|
|
7199
|
-
logger = inject(DynamicFormLogger);
|
|
7200
|
-
hostInjector = inject(Injector);
|
|
7201
|
-
addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
7202
|
-
fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
7203
|
-
hidden = input(undefined, ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
7204
|
-
slotAttr = computed(() => this.addon().slot, ...(ngDevMode ? [{ debugName: "slotAttr" }] : /* istanbul ignore next */ []));
|
|
7205
|
-
className = computed(() => this.addon().className ?? null, ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
7206
|
-
_hiddenByValue = new WeakMap();
|
|
7207
|
-
_resolvedHidden = signal(undefined, ...(ngDevMode ? [{ debugName: "_resolvedHidden" }] : /* istanbul ignore next */ []));
|
|
7208
|
-
isHidden = computed(() => {
|
|
7209
|
-
const pre = this.hidden();
|
|
7210
|
-
if (pre)
|
|
7211
|
-
return pre();
|
|
7212
|
-
const local = this._resolvedHidden();
|
|
7213
|
-
return local ? local() : false;
|
|
7214
|
-
}, ...(ngDevMode ? [{ debugName: "isHidden" }] : /* istanbul ignore next */ []));
|
|
7215
|
-
asyncLoaded = signal(undefined, ...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
|
|
7216
|
-
resolvedComponent = computed(() => {
|
|
7217
|
-
const kind = this.addon().kind;
|
|
7218
|
-
const cached = this.registry.getLoadedKindComponent(kind);
|
|
7219
|
-
if (cached)
|
|
7220
|
-
return cached;
|
|
7221
|
-
const loaded = this.asyncLoaded();
|
|
7222
|
-
return loaded?.kind === kind ? loaded.component : undefined;
|
|
7223
|
-
}, ...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
|
|
7224
|
-
kindInputs = computed(() => ({
|
|
7225
|
-
addon: this.addon(),
|
|
7226
|
-
fieldInputs: this.fieldInputs(),
|
|
7227
|
-
}), ...(ngDevMode ? [{ debugName: "kindInputs" }] : /* istanbul ignore next */ []));
|
|
7228
|
-
constructor() {
|
|
7229
|
-
explicitEffect([this.addon], ([addon]) => {
|
|
7230
|
-
const h = addon.hidden;
|
|
7231
|
-
const valueKey = typeof h === 'object' && h !== null ? h : null;
|
|
7232
|
-
let resolved = valueKey ? this._hiddenByValue.get(valueKey) : undefined;
|
|
7233
|
-
if (!resolved) {
|
|
7234
|
-
resolved = resolveDynamicValue(addon.hidden, false, this.hostInjector);
|
|
7235
|
-
if (valueKey)
|
|
7236
|
-
this._hiddenByValue.set(valueKey, resolved);
|
|
7237
|
-
}
|
|
7238
|
-
this._resolvedHidden.set(resolved);
|
|
7239
|
-
});
|
|
7240
|
-
explicitEffect([this.addon], ([addon]) => {
|
|
7241
|
-
const kind = addon.kind;
|
|
7242
|
-
if (this.registry.getLoadedKindComponent(kind))
|
|
7243
|
-
return; this.registry
|
|
7244
|
-
.loadKindComponent(kind)
|
|
7245
|
-
.then((component) => this.asyncLoaded.set({ kind, component }))
|
|
7246
|
-
.catch((error) => {
|
|
7247
|
-
this.logger.warn(`Failed to load addon kind '${kind}': ${error instanceof Error ? error.message : String(error)}. ` +
|
|
7248
|
-
`Registered kinds: ${this.registry.getKindNames().join(', ') || '(none)'}.`);
|
|
7249
|
-
});
|
|
7250
|
-
});
|
|
7251
|
-
}
|
|
7252
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfAddonSlot, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7253
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DfAddonSlot, isStandalone: true, selector: "df-addon-slot", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.slot": "slotAttr()", "class": "className()", "style.display": "isHidden() ? \"none\" : null" } }, ngImport: i0, template: `
|
|
7254
|
-
@if (resolvedComponent(); as cmp) {
|
|
7255
|
-
<ng-container *ngComponentOutlet="cmp; inputs: kindInputs()" />
|
|
7256
|
-
}
|
|
7257
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7258
|
-
}
|
|
7259
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DfAddonSlot, decorators: [{
|
|
7260
|
-
type: Component,
|
|
7261
|
-
args: [{
|
|
7262
|
-
selector: 'df-addon-slot',
|
|
7263
|
-
imports: [NgComponentOutlet],
|
|
7264
|
-
template: `
|
|
7265
|
-
@if (resolvedComponent(); as cmp) {
|
|
7266
|
-
<ng-container *ngComponentOutlet="cmp; inputs: kindInputs()" />
|
|
7267
|
-
}
|
|
7268
|
-
`,
|
|
7269
|
-
host: {
|
|
7270
|
-
'[attr.slot]': 'slotAttr()',
|
|
7271
|
-
'[class]': 'className()',
|
|
7272
|
-
'[style.display]': 'isHidden() ? "none" : null',
|
|
7273
|
-
},
|
|
7274
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7275
|
-
}]
|
|
7276
|
-
}], ctorParameters: () => [], propDecorators: { addon: [{ type: i0.Input, args: [{ isSignal: true, alias: "addon", required: true }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }] } });
|
|
7277
|
-
|
|
7278
4913
|
class TextAddonComponent {
|
|
7279
|
-
addon = input.required(
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
}
|
|
7284
|
-
i0.ɵɵ
|
|
4914
|
+
addon = input.required(/* @ts-ignore */
|
|
4915
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
4916
|
+
fieldInputs = input(/* @ts-ignore */
|
|
4917
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
4918
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TextAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4919
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", 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 });
|
|
4920
|
+
}
|
|
4921
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TextAddonComponent, decorators: [{
|
|
7285
4922
|
type: Component,
|
|
7286
4923
|
args: [{
|
|
7287
4924
|
selector: 'df-text-addon',
|
|
@@ -7302,14 +4939,18 @@ var textAddon_component = /*#__PURE__*/Object.freeze({
|
|
|
7302
4939
|
class TemplateAddonComponent {
|
|
7303
4940
|
templates = inject(DF_FIELD_TEMPLATES, { optional: true });
|
|
7304
4941
|
logger = inject(DynamicFormLogger);
|
|
7305
|
-
addon = input.required(
|
|
7306
|
-
|
|
4942
|
+
addon = input.required(/* @ts-ignore */
|
|
4943
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
4944
|
+
fieldInputs = input(/* @ts-ignore */
|
|
4945
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
7307
4946
|
template = computed(() => {
|
|
7308
4947
|
const map = this.templates?.();
|
|
7309
4948
|
const key = this.addon().templateKey;
|
|
7310
4949
|
return map?.get(key) ?? null;
|
|
7311
|
-
},
|
|
7312
|
-
|
|
4950
|
+
}, /* @ts-ignore */
|
|
4951
|
+
...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
|
|
4952
|
+
outletContext = computed(() => ({ $implicit: this.fieldInputs() }), /* @ts-ignore */
|
|
4953
|
+
...(ngDevMode ? [{ debugName: "outletContext" }] : /* istanbul ignore next */ []));
|
|
7313
4954
|
constructor() {
|
|
7314
4955
|
explicitEffect([this.template, this.addon], ([tpl, addon]) => {
|
|
7315
4956
|
if (!tpl) {
|
|
@@ -7317,14 +4958,14 @@ class TemplateAddonComponent {
|
|
|
7317
4958
|
}
|
|
7318
4959
|
});
|
|
7319
4960
|
}
|
|
7320
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
7321
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
4961
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TemplateAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4962
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
|
|
7322
4963
|
@if (template(); as tpl) {
|
|
7323
4964
|
<ng-container *ngTemplateOutlet="tpl; context: outletContext()" />
|
|
7324
4965
|
}
|
|
7325
4966
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7326
4967
|
}
|
|
7327
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
4968
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: TemplateAddonComponent, decorators: [{
|
|
7328
4969
|
type: Component,
|
|
7329
4970
|
args: [{
|
|
7330
4971
|
selector: 'df-template-addon',
|
|
@@ -7345,15 +4986,20 @@ var templateAddon_component = /*#__PURE__*/Object.freeze({
|
|
|
7345
4986
|
|
|
7346
4987
|
class ComponentAddonComponent {
|
|
7347
4988
|
logger = inject(DynamicFormLogger);
|
|
7348
|
-
addon = input.required(
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
4989
|
+
addon = input.required(/* @ts-ignore */
|
|
4990
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
4991
|
+
fieldInputs = input(/* @ts-ignore */
|
|
4992
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
4993
|
+
inputs = computed(() => (this.addon().inputs ?? {}), /* @ts-ignore */
|
|
4994
|
+
...(ngDevMode ? [{ debugName: "inputs" }] : /* istanbul ignore next */ []));
|
|
4995
|
+
asyncLoaded = signal(undefined, /* @ts-ignore */
|
|
4996
|
+
...(ngDevMode ? [{ debugName: "asyncLoaded" }] : /* istanbul ignore next */ []));
|
|
7352
4997
|
resolvedComponent = computed(() => {
|
|
7353
4998
|
const current = this.addon();
|
|
7354
4999
|
const loaded = this.asyncLoaded();
|
|
7355
5000
|
return loaded?.addon === current ? loaded.component : undefined;
|
|
7356
|
-
},
|
|
5001
|
+
}, /* @ts-ignore */
|
|
5002
|
+
...(ngDevMode ? [{ debugName: "resolvedComponent" }] : /* istanbul ignore next */ []));
|
|
7357
5003
|
constructor() {
|
|
7358
5004
|
explicitEffect([this.addon], ([addon]) => {
|
|
7359
5005
|
Promise.resolve(addon.component())
|
|
@@ -7367,14 +5013,14 @@ class ComponentAddonComponent {
|
|
|
7367
5013
|
});
|
|
7368
5014
|
});
|
|
7369
5015
|
}
|
|
7370
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
7371
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
5016
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ComponentAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5017
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
|
|
7372
5018
|
@if (resolvedComponent(); as cmp) {
|
|
7373
5019
|
<ng-container *ngComponentOutlet="cmp; inputs: inputs()" />
|
|
7374
5020
|
}
|
|
7375
5021
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7376
5022
|
}
|
|
7377
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5023
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ComponentAddonComponent, decorators: [{
|
|
7378
5024
|
type: Component,
|
|
7379
5025
|
args: [{
|
|
7380
5026
|
selector: 'df-component-addon',
|
|
@@ -7393,96 +5039,17 @@ var componentAddon_component = /*#__PURE__*/Object.freeze({
|
|
|
7393
5039
|
ComponentAddonComponent: ComponentAddonComponent
|
|
7394
5040
|
});
|
|
7395
5041
|
|
|
7396
|
-
async function runPresetAction(preset, ctx, collaborators, adapterLabel, fieldLabel) {
|
|
7397
|
-
if (ctx.form === null && preset !== 'copy' && preset !== 'paste') {
|
|
7398
|
-
collaborators.logger.warn(`preset '${String(preset)}' fired without a host field context — ignoring.`);
|
|
7399
|
-
return;
|
|
7400
|
-
}
|
|
7401
|
-
if (ctx.form !== null && (preset === 'clear' || preset === 'reset' || preset === 'paste') && ctx.form.disabled()) {
|
|
7402
|
-
collaborators.logger.warn(`preset '${String(preset)}' skipped — host '${fieldLabel}' is disabled.`);
|
|
7403
|
-
return;
|
|
7404
|
-
}
|
|
7405
|
-
switch (preset) {
|
|
7406
|
-
case 'clear': {
|
|
7407
|
-
const next = typeof ctx.value === 'string' ? '' : undefined;
|
|
7408
|
-
collaborators.fieldValueSetter?.(next);
|
|
7409
|
-
return;
|
|
7410
|
-
}
|
|
7411
|
-
case 'reset': {
|
|
7412
|
-
const defaultValue = collaborators.fieldDefaultValueGetter?.();
|
|
7413
|
-
collaborators.fieldValueSetter?.(defaultValue !== undefined ? defaultValue : typeof ctx.value === 'string' ? '' : undefined);
|
|
7414
|
-
return;
|
|
7415
|
-
}
|
|
7416
|
-
case 'paste': {
|
|
7417
|
-
if (typeof navigator === 'undefined' || !navigator.clipboard) {
|
|
7418
|
-
collaborators.logger.warn(`preset 'paste' requires a browser clipboard API (not available in this environment).`);
|
|
7419
|
-
return;
|
|
7420
|
-
}
|
|
7421
|
-
try {
|
|
7422
|
-
const text = await navigator.clipboard.readText();
|
|
7423
|
-
collaborators.fieldValueSetter?.(text);
|
|
7424
|
-
}
|
|
7425
|
-
catch (error) {
|
|
7426
|
-
collaborators.logger.warn(`preset 'paste' clipboard read failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
7427
|
-
}
|
|
7428
|
-
return;
|
|
7429
|
-
}
|
|
7430
|
-
case 'copy': {
|
|
7431
|
-
if (typeof navigator === 'undefined' || !navigator.clipboard) {
|
|
7432
|
-
collaborators.logger.warn(`preset 'copy' requires a browser clipboard API (not available in this environment).`);
|
|
7433
|
-
return;
|
|
7434
|
-
}
|
|
7435
|
-
try {
|
|
7436
|
-
const v = ctx.value;
|
|
7437
|
-
await navigator.clipboard.writeText(v == null ? '' : String(v));
|
|
7438
|
-
}
|
|
7439
|
-
catch (error) {
|
|
7440
|
-
collaborators.logger.warn(`preset 'copy' clipboard write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
7441
|
-
}
|
|
7442
|
-
return;
|
|
7443
|
-
}
|
|
7444
|
-
case 'toggle-password-visibility': {
|
|
7445
|
-
const override = collaborators.typeOverride;
|
|
7446
|
-
if (!override) {
|
|
7447
|
-
collaborators.logger.warn(`preset 'toggle-password-visibility' has no effect outside a '${fieldLabel}' field.`);
|
|
7448
|
-
return;
|
|
7449
|
-
}
|
|
7450
|
-
const baseline = collaborators.baselineType?.();
|
|
7451
|
-
if (baseline !== undefined && baseline !== 'password') {
|
|
7452
|
-
collaborators.logger.warn(`preset 'toggle-password-visibility' refused: host '${fieldLabel}' has type '${baseline}', not 'password'.`);
|
|
7453
|
-
return;
|
|
7454
|
-
}
|
|
7455
|
-
const current = override();
|
|
7456
|
-
override.set(current === 'text' ? 'password' : 'text');
|
|
7457
|
-
return;
|
|
7458
|
-
}
|
|
7459
|
-
default:
|
|
7460
|
-
collaborators.logger.warn(`preset '${String(preset)}' is not implemented in the ${adapterLabel} adapter.`);
|
|
7461
|
-
}
|
|
7462
|
-
}
|
|
7463
|
-
|
|
7464
|
-
function injectFieldsSupportingAddons() {
|
|
7465
|
-
const registry = inject(FIELD_REGISTRY);
|
|
7466
|
-
const entries = [];
|
|
7467
|
-
for (const [name, def] of registry.entries()) {
|
|
7468
|
-
if (def.addons === undefined)
|
|
7469
|
-
continue;
|
|
7470
|
-
entries.push({ name, slots: def.addons.slots, allowedKinds: def.addons.allowedKinds });
|
|
7471
|
-
}
|
|
7472
|
-
return entries;
|
|
7473
|
-
}
|
|
7474
|
-
|
|
7475
5042
|
function withCustomAddon(definition) {
|
|
7476
5043
|
return createFeature('addons', [
|
|
7477
5044
|
{
|
|
7478
|
-
provide:
|
|
5045
|
+
provide: ADDON_TYPE_DEFINITIONS,
|
|
7479
5046
|
useValue: definition,
|
|
7480
5047
|
multi: true,
|
|
7481
5048
|
},
|
|
7482
5049
|
]);
|
|
7483
5050
|
}
|
|
7484
5051
|
|
|
7485
|
-
function
|
|
5052
|
+
function withAddonActions(handlers) {
|
|
7486
5053
|
return createFeature('addon-actions', [
|
|
7487
5054
|
{
|
|
7488
5055
|
provide: ADDON_ACTION_HANDLERS,
|
|
@@ -7494,9 +5061,9 @@ function provideAddonActions(handlers) {
|
|
|
7494
5061
|
|
|
7495
5062
|
function sanitizeFormConfig(config, options = {}) {
|
|
7496
5063
|
const fieldRegistry = inject(FIELD_REGISTRY);
|
|
7497
|
-
const
|
|
5064
|
+
const typeRegistry = inject(ADDON_TYPE_REGISTRY);
|
|
7498
5065
|
const source = options.source ?? 'inline';
|
|
7499
|
-
const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry,
|
|
5066
|
+
const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry, typeRegistry, source);
|
|
7500
5067
|
return {
|
|
7501
5068
|
sanitized: { ...config, fields: fields },
|
|
7502
5069
|
warnings,
|
|
@@ -7512,9 +5079,9 @@ function logAddonWarnings(warnings, logger) {
|
|
|
7512
5079
|
}
|
|
7513
5080
|
}
|
|
7514
5081
|
}
|
|
7515
|
-
function sanitizeFormConfigPure(config, fieldRegistry,
|
|
5082
|
+
function sanitizeFormConfigPure(config, fieldRegistry, typeRegistry, options = {}) {
|
|
7516
5083
|
const source = options.source ?? 'inline';
|
|
7517
|
-
const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry,
|
|
5084
|
+
const { fields, warnings } = walkAndValidateAddons((config.fields ?? []), fieldRegistry, typeRegistry, source);
|
|
7518
5085
|
return { sanitized: { ...config, fields: fields }, warnings };
|
|
7519
5086
|
}
|
|
7520
5087
|
|
|
@@ -7531,94 +5098,7 @@ function arrayEvent(arrayKey) {
|
|
|
7531
5098
|
}
|
|
7532
5099
|
|
|
7533
5100
|
|
|
7534
|
-
function formConfig(config) {
|
|
7535
|
-
return config;
|
|
7536
|
-
}
|
|
7537
|
-
|
|
7538
|
-
const CONTAINER_TYPES = ['group', 'row', 'array', 'container'];
|
|
7539
|
-
const HIDDEN_ONLY_LOGIC_TYPES = ['group', 'row', 'array', 'container', 'page'];
|
|
7540
|
-
const OPTION_FIELD_TYPES = ['select', 'radio', 'multi-checkbox'];
|
|
7541
|
-
const SLIDER_TYPE = 'slider';
|
|
7542
|
-
const HIDDEN_TYPE = 'hidden';
|
|
7543
|
-
function createField(type, config) {
|
|
7544
|
-
const configWithProps = config;
|
|
7545
|
-
if (!configWithProps['key']) {
|
|
7546
|
-
throw new DynamicFormError(`createField('${type}'): 'key' property is required`);
|
|
7547
|
-
}
|
|
7548
|
-
if (CONTAINER_TYPES.includes(type)) {
|
|
7549
|
-
if ('label' in configWithProps && configWithProps['label'] !== undefined) {
|
|
7550
|
-
throw new DynamicFormError(`createField('${type}'): Container fields (${CONTAINER_TYPES.join(', ')}) do NOT support 'label'. ` +
|
|
7551
|
-
`Labels go on the child fields inside the container.`);
|
|
7552
|
-
}
|
|
7553
|
-
}
|
|
7554
|
-
if (HIDDEN_ONLY_LOGIC_TYPES.includes(type)) {
|
|
7555
|
-
if ('logic' in configWithProps && configWithProps['logic'] !== undefined) {
|
|
7556
|
-
const logic = configWithProps['logic'];
|
|
7557
|
-
if (Array.isArray(logic)) {
|
|
7558
|
-
const nonHiddenLogic = logic.filter((l) => l.type !== 'hidden');
|
|
7559
|
-
if (nonHiddenLogic.length > 0) {
|
|
7560
|
-
throw new DynamicFormError(`createField('${type}'): Only 'hidden' logic type is supported. ` +
|
|
7561
|
-
`Found unsupported logic types: ${nonHiddenLogic.map((l) => l.type).join(', ')}. ` +
|
|
7562
|
-
`For other logic types, apply them to child fields instead.`);
|
|
7563
|
-
}
|
|
7564
|
-
}
|
|
7565
|
-
}
|
|
7566
|
-
}
|
|
7567
|
-
if (OPTION_FIELD_TYPES.includes(type)) {
|
|
7568
|
-
const props = configWithProps['props'];
|
|
7569
|
-
if (props && 'options' in props) {
|
|
7570
|
-
throw new DynamicFormError(`createField('${type}'): 'options' must be at FIELD level, NOT inside 'props'. ` +
|
|
7571
|
-
`Move 'options' from props to the field root: { type: '${type}', key: '...', options: [...] }`);
|
|
7572
|
-
}
|
|
7573
|
-
}
|
|
7574
|
-
if (type === SLIDER_TYPE) {
|
|
7575
|
-
const props = configWithProps['props'];
|
|
7576
|
-
if (props) {
|
|
7577
|
-
if ('min' in props || 'max' in props) {
|
|
7578
|
-
throw new DynamicFormError(`createField('slider'): Use 'minValue' and 'maxValue' at FIELD level, NOT 'min'/'max' in props. ` +
|
|
7579
|
-
`Example: { type: 'slider', key: '...', minValue: 0, maxValue: 100, step: 1 }`);
|
|
7580
|
-
}
|
|
7581
|
-
if ('step' in props) {
|
|
7582
|
-
throw new DynamicFormError(`createField('slider'): Use 'step' at FIELD level, NOT in props. ` +
|
|
7583
|
-
`Example: { type: 'slider', key: '...', minValue: 0, maxValue: 100, step: 1 }`);
|
|
7584
|
-
}
|
|
7585
|
-
}
|
|
7586
|
-
}
|
|
7587
|
-
if (type === HIDDEN_TYPE) {
|
|
7588
|
-
if ('logic' in configWithProps && configWithProps['logic'] !== undefined) {
|
|
7589
|
-
throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'logic'. ` + `Hidden fields are purely for passing values through the form.`);
|
|
7590
|
-
}
|
|
7591
|
-
if ('validators' in configWithProps && configWithProps['validators'] !== undefined) {
|
|
7592
|
-
throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'validators'. ` +
|
|
7593
|
-
`Hidden fields are purely for passing values through the form.`);
|
|
7594
|
-
}
|
|
7595
|
-
if ('required' in configWithProps && configWithProps['required']) {
|
|
7596
|
-
throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'required' validation. ` +
|
|
7597
|
-
`Hidden fields are purely for passing values through the form.`);
|
|
7598
|
-
}
|
|
7599
|
-
if ('label' in configWithProps && configWithProps['label'] !== undefined) {
|
|
7600
|
-
throw new DynamicFormError(`createField('hidden'): Hidden fields do NOT support 'label'. ` +
|
|
7601
|
-
`Hidden fields are not rendered and have no visual representation.`);
|
|
7602
|
-
}
|
|
7603
|
-
}
|
|
7604
|
-
if (type === 'array') {
|
|
7605
|
-
if ('template' in configWithProps) {
|
|
7606
|
-
throw new DynamicFormError(`createField('array'): Use 'fields' (NOT 'template') to define the array item structure. ` +
|
|
7607
|
-
`Example: { type: 'array', key: '...', fields: [{ type: 'group', key: 'item', fields: [...] }] }`);
|
|
7608
|
-
}
|
|
7609
|
-
}
|
|
7610
|
-
if (type === 'button') {
|
|
7611
|
-
if (!('event' in configWithProps) || configWithProps['event'] === undefined) {
|
|
7612
|
-
throw new DynamicFormError(`createField('button'): Generic buttons REQUIRE the 'event' property with a FormEventConstructor. ` +
|
|
7613
|
-
`For common actions, use 'submit', 'next', or 'previous' instead which don't require event configuration. ` +
|
|
7614
|
-
`Example: { type: 'submit', key: 'submit', label: 'Submit' }`);
|
|
7615
|
-
}
|
|
7616
|
-
}
|
|
7617
|
-
return { type, ...config };
|
|
7618
|
-
}
|
|
7619
|
-
const field = createField;
|
|
7620
|
-
|
|
7621
5101
|
|
|
7622
5102
|
|
|
7623
|
-
export {
|
|
7624
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
5103
|
+
export { withValueExclusionDefaults as $, ArrayItemRegistryService as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DfFieldOutlet as D, EventDispatcher as E, FormStateManager as F, PrependArrayItemEvent as G, PreviousPageEvent as H, InsertArrayItemEvent as I, TextAddonComponent as J, arrayEvent as K, createWrappers as L, MoveArrayItemEvent as M, NextPageEvent as N, isWrappersBundle as O, PROPERTY_OVERRIDE_STORE as P, provideDynamicForm as Q, RemoveAtIndexEvent as R, ShiftArrayItemEvent as S, TemplateAddonComponent as T, sanitizeFormConfig as U, withAddonActions as V, withCustomAddon as W, withEventFormValue as X, withLegacyStatusClasses as Y, withLoggerConfig as Z, withValidationExecutionDefaults as _, isSameWrapperChain as a, wrapperProps as a0, createWrapperChainController as b, createFieldResolutionPipe as c, createSchemaFromFields as d, createHiddenSignal as e, createRenderReadySignal as f, getNormalizedArrayMetadata as g, getFieldDefaultValue as h, injectFieldRegistry as i, DEV_MODE as j, DEFAULT_DEBOUNCE_MS as k, DERIVATION_WARNING_TRACKER as l, mapFieldToInputs as m, DERIVATION_LOG_CONFIG as n, AppendArrayItemEvent as o, ComponentAddonComponent as p, ConsoleLogger as q, resolveWrappers as r, DfTemplate as s, traverseFieldsWithContext as t, DynamicForm as u, FormClearEvent as v, FormResetEvent as w, FormSubmitEvent as x, PageChangeEvent as y, PopArrayItemEvent as z };
|
|
5104
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-BJi_rtey.mjs.map
|