@ng-forge/dynamic-forms 0.1.1 → 0.1.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 +33 -0
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-Dcd04rfS.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWj75LVN.mjs} +116 -116
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-Dcd04rfS.mjs.map → ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWj75LVN.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-DjrahUgb.mjs → ng-forge-dynamic-forms-page-field.component-ZVrJUMNb.mjs} +18 -9
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-DjrahUgb.mjs.map → ng-forge-dynamic-forms-page-field.component-ZVrJUMNb.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-7QVNm95W.mjs → ng-forge-dynamic-forms-text-field.component-BKTbxAKn.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-7QVNm95W.mjs.map → ng-forge-dynamic-forms-text-field.component-BKTbxAKn.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +9 -3
- package/types/ng-forge-dynamic-forms.d.ts +152 -93
|
@@ -3,7 +3,7 @@ import { inject, DestroyRef, Injector, input, computed, ChangeDetectionStrategy,
|
|
|
3
3
|
import { NgComponentOutlet } from '@angular/common';
|
|
4
4
|
import { outputFromObservable } from '@angular/core/rxjs-interop';
|
|
5
5
|
import { pipe, switchMap, of, forkJoin, map, scan } from 'rxjs';
|
|
6
|
-
import { i as injectFieldRegistry, E as EventBus, v as validatePageNesting, d as derivedFromDeferred, r as resolveField, a as reconcileFields, e as emitComponentInitialized } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
6
|
+
import { i as injectFieldRegistry, E as EventBus, v as validatePageNesting, d as derivedFromDeferred, r as resolveField, a as reconcileFields, e as emitComponentInitialized } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWj75LVN.mjs';
|
|
7
7
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -76,13 +76,22 @@ class PageFieldComponent {
|
|
|
76
76
|
return forkJoin(fields.map((f) => resolveField(f, context)));
|
|
77
77
|
}), map((fields) => fields.filter((f) => f !== undefined)), scan(reconcileFields, [])), { initialValue: [], injector: this.injector });
|
|
78
78
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
79
|
-
//
|
|
79
|
+
// Constructor
|
|
80
80
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
constructor() {
|
|
82
|
+
this.setupEffects();
|
|
83
|
+
}
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
// Private Methods
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
setupEffects() {
|
|
88
|
+
// Emit initialization event when fields are resolved
|
|
89
|
+
explicitEffect([this.resolvedFields], ([fields]) => {
|
|
90
|
+
if (fields.length > 0) {
|
|
91
|
+
emitComponentInitialized(this.eventBus, 'page', this.field().key, this.injector);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
86
95
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.5", ngImport: i0, type: PageFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
87
96
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.5", type: PageFieldComponent, isStandalone: true, selector: "section[page-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: true, transformFunction: null }, isVisible: { classPropertyName: "isVisible", publicName: "isVisible", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { nextPage: "nextPage", previousPage: "previousPage", pageChange: "pageChange" }, host: { properties: { "class.disabled": "disabled()", "class.df-page-visible": "isVisible()", "class.df-page-hidden": "!isVisible()", "attr.aria-hidden": "!isVisible()", "attr.data-page-index": "pageIndex()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-page-field" }, ngImport: i0, template: `
|
|
88
97
|
@for (field of resolvedFields(); track field.key) {
|
|
@@ -106,7 +115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.5", ngImpor
|
|
|
106
115
|
'[id]': '`${key()}`',
|
|
107
116
|
'[attr.data-testid]': 'key()',
|
|
108
117
|
}, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md);--df-grid-row-gap: var(--df-grid-row-gap-md)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;width:100%;gap:var(--df-grid-row-gap);transition:opacity .2s ease-in-out}:host.df-page-spacing-custom{--df-grid-row-gap: var(--page-custom-spacing, 1.5rem)}:host.df-page-visible{opacity:1}:host.df-page-hidden{display:none;opacity:0;pointer-events:none}:host[aria-hidden=true]{pointer-events:none;visibility:hidden}@media(prefers-reduced-motion:no-preference){:host{transition:opacity .3s ease-in-out,transform .3s ease-in-out}:host.df-page-visible{transform:translate(0)}:host.df-page-hidden{transform:translate(-10px)}}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
|
|
109
|
-
}], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: true }] }], isVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVisible", required: true }] }], nextPage: [{ type: i0.Output, args: ["nextPage"] }], previousPage: [{ type: i0.Output, args: ["previousPage"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
|
|
118
|
+
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: true }] }], isVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVisible", required: true }] }], nextPage: [{ type: i0.Output, args: ["nextPage"] }], previousPage: [{ type: i0.Output, args: ["previousPage"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
|
|
110
119
|
|
|
111
120
|
export { PageFieldComponent as default };
|
|
112
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-
|
|
121
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-ZVrJUMNb.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-forge-dynamic-forms-page-field.component-DjrahUgb.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/page/page-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, Injector, input } from '@angular/core';\nimport { NgComponentOutlet } from '@angular/common';\nimport { outputFromObservable } from '@angular/core/rxjs-interop';\nimport { forkJoin, map, of, pipe, scan, switchMap } from 'rxjs';\nimport { derivedFromDeferred } from '../../utils/derived-from-deferred/derived-from-deferred';\nimport { reconcileFields, ResolvedField, resolveField } from '../../utils/resolve-field/resolve-field';\nimport { emitComponentInitialized } from '../../utils/emit-initialization/emit-initialization';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { PageField, validatePageNesting } from '../../definitions/default/page-field';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { EventBus } from '../../events/event.bus';\nimport { NextPageEvent, PageChangeEvent, PreviousPageEvent } from '../../events/constants';\nimport { FieldDef } from '../../definitions/base/field-def';\n\n/**\n * Renders a single page in multi-page (wizard) forms.\n *\n * Visibility is controlled by the PageOrchestrator via the isVisible input.\n * Pages cannot be nested within other pages - validation prevents this.\n * Field values are flattened into the parent form (no nesting under page key).\n */\n@Component({\n selector: 'section[page-field]',\n imports: [NgComponentOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n <ng-container *ngComponentOutlet=\"field.component; injector: field.injector; inputs: field.inputs()\" />\n }\n `,\n styleUrl: './page-field.component.scss',\n host: {\n class: 'df-field df-page-field',\n '[class.disabled]': 'disabled()',\n '[class.df-page-visible]': 'isVisible()',\n '[class.df-page-hidden]': '!isVisible()',\n '[attr.aria-hidden]': '!isVisible()',\n '[attr.data-page-index]': 'pageIndex()',\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class PageFieldComponent {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly injector = inject(Injector);\n private readonly eventBus = inject(EventBus);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<PageField>();\n key = input.required<string>();\n pageIndex = input.required<number>();\n isVisible = input.required<boolean>();\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly disabled = computed(() => this.field().disabled || false);\n\n readonly isValid = computed(() => {\n const pageField = this.field();\n const valid = validatePageNesting(pageField);\n\n if (!valid) {\n console.error(\n `[Dynamic Forms] Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +\n `Pages cannot contain other pages. Consider using groups or rows for nested structure.`,\n pageField,\n );\n }\n\n return valid;\n });\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly nextPage = outputFromObservable(this.eventBus.on<NextPageEvent>('next-page'));\n readonly previousPage = outputFromObservable(this.eventBus.on<PreviousPageEvent>('previous-page'));\n readonly pageChange = outputFromObservable(this.eventBus.on<PageChangeEvent>('page-change'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => {\n if (!this.isValid()) {\n return [];\n }\n return this.field().fields || [];\n });\n\n protected readonly resolvedFields = derivedFromDeferred(\n this.fieldsSource,\n pipe(\n switchMap((fields) => {\n if (!fields || fields.length === 0) {\n return of([] as (ResolvedField | undefined)[]);\n }\n const pageKey = this.field().key;\n const context = {\n loadTypeComponent: (type: string) => this.fieldRegistry.loadTypeComponent(type),\n registry: this.rawFieldRegistry(),\n injector: this.injector,\n destroyRef: this.destroyRef,\n onError: (fieldDef: FieldDef<unknown>, error: unknown) => {\n const fieldKey = fieldDef.key || '<no key>';\n console.error(\n `[Dynamic Forms] Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within page '${pageKey}'. Ensure the field type is registered in your field registry.`,\n error,\n );\n },\n };\n return forkJoin(fields.map((f) => resolveField(f as FieldDef<unknown>, context)));\n }),\n map((fields) => fields.filter((f): f is ResolvedField => f !== undefined)),\n scan(reconcileFields, [] as ResolvedField[]),\n ),\n { initialValue: [] as ResolvedField[], injector: this.injector },\n );\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Effects\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly emitInitializedOnFieldsResolved = explicitEffect([this.resolvedFields], ([fields]) => {\n if (fields.length > 0) {\n emitComponentInitialized(this.eventBus, 'page', this.field().key, this.injector);\n }\n });\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ng-forge-dynamic-forms-page-field.component-ZVrJUMNb.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/page/page-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, Injector, input } from '@angular/core';\nimport { NgComponentOutlet } from '@angular/common';\nimport { outputFromObservable } from '@angular/core/rxjs-interop';\nimport { forkJoin, map, of, pipe, scan, switchMap } from 'rxjs';\nimport { derivedFromDeferred } from '../../utils/derived-from-deferred/derived-from-deferred';\nimport { reconcileFields, ResolvedField, resolveField } from '../../utils/resolve-field/resolve-field';\nimport { emitComponentInitialized } from '../../utils/emit-initialization/emit-initialization';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { PageField, validatePageNesting } from '../../definitions/default/page-field';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { EventBus } from '../../events/event.bus';\nimport { NextPageEvent, PageChangeEvent, PreviousPageEvent } from '../../events/constants';\nimport { FieldDef } from '../../definitions/base/field-def';\n\n/**\n * Renders a single page in multi-page (wizard) forms.\n *\n * Visibility is controlled by the PageOrchestrator via the isVisible input.\n * Pages cannot be nested within other pages - validation prevents this.\n * Field values are flattened into the parent form (no nesting under page key).\n */\n@Component({\n selector: 'section[page-field]',\n imports: [NgComponentOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n <ng-container *ngComponentOutlet=\"field.component; injector: field.injector; inputs: field.inputs()\" />\n }\n `,\n styleUrl: './page-field.component.scss',\n host: {\n class: 'df-field df-page-field',\n '[class.disabled]': 'disabled()',\n '[class.df-page-visible]': 'isVisible()',\n '[class.df-page-hidden]': '!isVisible()',\n '[attr.aria-hidden]': '!isVisible()',\n '[attr.data-page-index]': 'pageIndex()',\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class PageFieldComponent {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly injector = inject(Injector);\n private readonly eventBus = inject(EventBus);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<PageField>();\n key = input.required<string>();\n pageIndex = input.required<number>();\n isVisible = input.required<boolean>();\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly disabled = computed(() => this.field().disabled || false);\n\n readonly isValid = computed(() => {\n const pageField = this.field();\n const valid = validatePageNesting(pageField);\n\n if (!valid) {\n console.error(\n `[Dynamic Forms] Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +\n `Pages cannot contain other pages. Consider using groups or rows for nested structure.`,\n pageField,\n );\n }\n\n return valid;\n });\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly nextPage = outputFromObservable(this.eventBus.on<NextPageEvent>('next-page'));\n readonly previousPage = outputFromObservable(this.eventBus.on<PreviousPageEvent>('previous-page'));\n readonly pageChange = outputFromObservable(this.eventBus.on<PageChangeEvent>('page-change'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => {\n if (!this.isValid()) {\n return [];\n }\n return this.field().fields || [];\n });\n\n protected readonly resolvedFields = derivedFromDeferred(\n this.fieldsSource,\n pipe(\n switchMap((fields) => {\n if (!fields || fields.length === 0) {\n return of([] as (ResolvedField | undefined)[]);\n }\n const pageKey = this.field().key;\n const context = {\n loadTypeComponent: (type: string) => this.fieldRegistry.loadTypeComponent(type),\n registry: this.rawFieldRegistry(),\n injector: this.injector,\n destroyRef: this.destroyRef,\n onError: (fieldDef: FieldDef<unknown>, error: unknown) => {\n const fieldKey = fieldDef.key || '<no key>';\n console.error(\n `[Dynamic Forms] Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within page '${pageKey}'. Ensure the field type is registered in your field registry.`,\n error,\n );\n },\n };\n return forkJoin(fields.map((f) => resolveField(f as FieldDef<unknown>, context)));\n }),\n map((fields) => fields.filter((f): f is ResolvedField => f !== undefined)),\n scan(reconcileFields, [] as ResolvedField[]),\n ),\n { initialValue: [] as ResolvedField[], injector: this.injector },\n );\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Constructor\n // ─────────────────────────────────────────────────────────────────────────────\n\n constructor() {\n this.setupEffects();\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Private Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n private setupEffects(): void {\n // Emit initialization event when fields are resolved\n explicitEffect([this.resolvedFields], ([fields]) => {\n if (fields.length > 0) {\n emitComponentInitialized(this.eventBus, 'page', this.field().key, this.injector);\n }\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { AsyncPipe } from '@angular/common';
|
|
4
|
-
import { D as DynamicTextPipe } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
4
|
+
import { D as DynamicTextPipe } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWj75LVN.mjs';
|
|
5
5
|
|
|
6
6
|
class TextFieldComponent {
|
|
7
7
|
key = input.required(...(ngDevMode ? [{ debugName: "key" }] : []));
|
|
@@ -110,4 +110,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.5", ngImpor
|
|
|
110
110
|
}], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
|
|
111
111
|
|
|
112
112
|
export { TextFieldComponent as default };
|
|
113
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-text-field.component-
|
|
113
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-text-field.component-BKTbxAKn.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-forge-dynamic-forms-text-field.component-
|
|
1
|
+
{"version":3,"file":"ng-forge-dynamic-forms-text-field.component-BKTbxAKn.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/text/text-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { AsyncPipe } from '@angular/common';\nimport { DynamicTextPipe } from '../../pipes/dynamic-text/dynamic-text.pipe';\nimport { TextProps } from '../../definitions/default/text-field';\nimport { DynamicText } from '../../models/types/dynamic-text';\n\n@Component({\n selector: 'df-text',\n template: `\n @switch (elementType()) {\n @case ('p') {\n <p class=\"df-text df-text-p\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </p>\n }\n @case ('h1') {\n <h1 class=\"df-text df-text-h1\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h1>\n }\n @case ('h2') {\n <h2 class=\"df-text df-text-h2\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h2>\n }\n @case ('h3') {\n <h3 class=\"df-text df-text-h3\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h3>\n }\n @case ('h4') {\n <h4 class=\"df-text df-text-h4\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h4>\n }\n @case ('h5') {\n <h5 class=\"df-text df-text-h5\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h5>\n }\n @case ('h6') {\n <h6 class=\"df-text df-text-h6\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h6>\n }\n @case ('span') {\n <span class=\"df-text df-text-span\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </span>\n }\n }\n `,\n styleUrl: './text-field.component.scss',\n imports: [AsyncPipe, DynamicTextPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n '[attr.hidden]': 'hidden() || null',\n },\n})\nexport default class TextFieldComponent {\n readonly key = input.required<string>();\n readonly label = input.required<DynamicText>();\n readonly className = input<string>();\n readonly tabIndex = input<number>();\n readonly hidden = input<boolean>();\n readonly props = input<TextProps>();\n\n readonly elementType = computed(() => this.props()?.elementType || 'p');\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { O as ARRAY_CONTEXT, A as AddArrayItemEvent, W as ArrayFieldComponent, a0 as BUILT_IN_FIELDS, b as DynamicForm, D as DynamicTextPipe, E as EventBus, L as FIELD_REGISTRY, Q as FIELD_SIGNAL_CONTEXT, F as FormClearEvent, u as FormResetEvent, X as GroupFieldComponent, N as NextPageEvent, P as PageChangeEvent, w as PreviousPageEvent, R as RemoveArrayItemEvent, Y as RowFieldComponent, S as SubmitEvent, Z as applyValidator, _ as applyValidators, x as arrayFieldMapper, y as baseFieldMapper, z as buildBaseInputs, B as checkboxFieldMapper, T as createResolvedErrorsSignal, C as datepickerFieldMapper, V as dynamicTextToObservable, a1 as getChildFieldTree, a2 as getChildrenMap, a3 as getFieldProxy, M as getFieldValueHandling, G as groupFieldMapper, a4 as hasFormStructure, i as injectFieldRegistry, k as isArrayField, h as isCheckedField, l as isContainerField, m as isDisplayOnlyField, c as isFormStateCondition, n as isGroupField, o as isLeafField, q as isPageField, s as isRowField, t as isValueBearingField, j as isValueField, I as optionsFieldMapper, H as pageFieldMapper, p as provideDynamicForm, g as resolveNextButtonDisabled, f as resolveSubmitButtonDisabled, $ as resolveTokens, J as rowFieldMapper, U as shouldShowErrors, K as valueFieldMapper } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
1
|
+
export { O as ARRAY_CONTEXT, A as AddArrayItemEvent, W as ArrayFieldComponent, a0 as BUILT_IN_FIELDS, b as DynamicForm, D as DynamicTextPipe, E as EventBus, L as FIELD_REGISTRY, Q as FIELD_SIGNAL_CONTEXT, F as FormClearEvent, u as FormResetEvent, X as GroupFieldComponent, N as NextPageEvent, P as PageChangeEvent, w as PreviousPageEvent, R as RemoveArrayItemEvent, Y as RowFieldComponent, S as SubmitEvent, Z as applyValidator, _ as applyValidators, x as arrayFieldMapper, y as baseFieldMapper, z as buildBaseInputs, B as checkboxFieldMapper, T as createResolvedErrorsSignal, C as datepickerFieldMapper, V as dynamicTextToObservable, a1 as getChildFieldTree, a2 as getChildrenMap, a3 as getFieldProxy, M as getFieldValueHandling, G as groupFieldMapper, a4 as hasFormStructure, i as injectFieldRegistry, k as isArrayField, h as isCheckedField, l as isContainerField, m as isDisplayOnlyField, c as isFormStateCondition, n as isGroupField, o as isLeafField, q as isPageField, s as isRowField, t as isValueBearingField, j as isValueField, I as optionsFieldMapper, H as pageFieldMapper, p as provideDynamicForm, g as resolveNextButtonDisabled, f as resolveSubmitButtonDisabled, $ as resolveTokens, J as rowFieldMapper, U as shouldShowErrors, K as valueFieldMapper } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-BWj75LVN.mjs';
|
|
2
2
|
//# sourceMappingURL=ng-forge-dynamic-forms.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-forge/dynamic-forms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Type-safe, signal-powered dynamic forms for Angular. Build complex forms from configuration with full TypeScript inference.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,8 +24,15 @@
|
|
|
24
24
|
"form-generator",
|
|
25
25
|
"type-safe",
|
|
26
26
|
"typescript",
|
|
27
|
-
"angular-forms"
|
|
27
|
+
"angular-forms",
|
|
28
|
+
"schema-forms",
|
|
29
|
+
"json-forms",
|
|
30
|
+
"config-driven"
|
|
28
31
|
],
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
29
36
|
"peerDependencies": {
|
|
30
37
|
"@angular/common": "~21.0.5",
|
|
31
38
|
"@angular/core": "~21.0.5",
|
|
@@ -44,7 +51,6 @@
|
|
|
44
51
|
"default": "./fesm2022/ng-forge-dynamic-forms.mjs"
|
|
45
52
|
}
|
|
46
53
|
},
|
|
47
|
-
"sideEffects": false,
|
|
48
54
|
"dependencies": {
|
|
49
55
|
"tslib": "^2.3.0"
|
|
50
56
|
}
|
|
@@ -654,6 +654,57 @@ type RegisteredFieldTypes = ContainerFieldTypes | LeafFieldTypes;
|
|
|
654
654
|
* Extract field types that are available in the registry
|
|
655
655
|
*/
|
|
656
656
|
type AvailableFieldTypes = keyof DynamicFormFieldRegistry['containers'] | keyof DynamicFormFieldRegistry['leaves'];
|
|
657
|
+
/**
|
|
658
|
+
* Combined registry mapping type names to field definitions.
|
|
659
|
+
* This flattens containers and leaves into a single mapping.
|
|
660
|
+
*/
|
|
661
|
+
type FieldTypeMap = DynamicFormFieldRegistry['containers'] & DynamicFormFieldRegistry['leaves'];
|
|
662
|
+
/**
|
|
663
|
+
* Extract a specific field type from RegisteredFieldTypes based on the `type` discriminant.
|
|
664
|
+
* This enables proper type narrowing when defining fields.
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```typescript
|
|
668
|
+
* // Extract a specific field type
|
|
669
|
+
* type MyInputField = ExtractField<'input'>;
|
|
670
|
+
*
|
|
671
|
+
* // Use in field definitions for proper props inference
|
|
672
|
+
* const field: ExtractField<'input'> = {
|
|
673
|
+
* type: 'input',
|
|
674
|
+
* key: 'email',
|
|
675
|
+
* value: '',
|
|
676
|
+
* props: { type: 'email' } // Only input props allowed here
|
|
677
|
+
* };
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
type ExtractField<T extends AvailableFieldTypes> = T extends keyof FieldTypeMap ? FieldTypeMap[T] : never;
|
|
681
|
+
/**
|
|
682
|
+
* Narrow a field definition based on its `type` property.
|
|
683
|
+
* Use this to get proper type inference when working with field unions.
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```typescript
|
|
687
|
+
* function processField<T extends RegisteredFieldTypes>(field: T): NarrowField<T> {
|
|
688
|
+
* return field as NarrowField<T>;
|
|
689
|
+
* }
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
type NarrowField<T> = T extends {
|
|
693
|
+
type: infer TType;
|
|
694
|
+
} ? (TType extends AvailableFieldTypes ? ExtractField<TType> : T) : T;
|
|
695
|
+
/**
|
|
696
|
+
* Narrow each field in an array based on its `type` property.
|
|
697
|
+
* Use with `satisfies` to get proper type inference for field arrays.
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* ```typescript
|
|
701
|
+
* const fields = [
|
|
702
|
+
* { type: 'input', key: 'name', value: '', props: { type: 'text' } },
|
|
703
|
+
* { type: 'select', key: 'country', value: 'us', options: [...] },
|
|
704
|
+
* ] as const satisfies NarrowFields;
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
707
|
+
type NarrowFields = readonly NarrowField<RegisteredFieldTypes>[];
|
|
657
708
|
|
|
658
709
|
/**
|
|
659
710
|
* Helper type to convert union to intersection
|
|
@@ -664,27 +715,49 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
|
664
715
|
* Depth counter for recursion limiting (prevents infinite type instantiation)
|
|
665
716
|
*/
|
|
666
717
|
type Depth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
718
|
+
/**
|
|
719
|
+
* Widens literal types to their primitive equivalents.
|
|
720
|
+
* This prevents `as const` from over-narrowing types like `''` to literal `''` instead of `string`.
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
* ```typescript
|
|
724
|
+
* type A = Widen<''>; // string
|
|
725
|
+
* type B = Widen<false>; // boolean
|
|
726
|
+
* type C = Widen<42>; // number
|
|
727
|
+
* type D = Widen<string[]>; // string[]
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
731
|
+
/**
|
|
732
|
+
* Infer value type based on field type and props.
|
|
733
|
+
* - Slider fields: always number
|
|
734
|
+
* - Input fields with props.type: 'number': number
|
|
735
|
+
* - Other fields: widen the literal value type
|
|
736
|
+
*/
|
|
737
|
+
type InferValueType<T, V> = T extends {
|
|
738
|
+
type: 'slider';
|
|
739
|
+
} ? number : T extends {
|
|
740
|
+
type: 'input';
|
|
741
|
+
props: {
|
|
742
|
+
type: 'number';
|
|
743
|
+
};
|
|
744
|
+
} ? number : Widen<V>;
|
|
745
|
+
/** Make type optional if field is not required */
|
|
746
|
+
type MaybeOptional<T, V> = T extends {
|
|
747
|
+
required: true;
|
|
748
|
+
} ? V : V | undefined;
|
|
667
749
|
/**
|
|
668
750
|
* Process a single field and determine its contribution to the form value type
|
|
669
|
-
* Handles:
|
|
670
|
-
* - Page fields: flatten children (valueHandling: 'flatten')
|
|
671
|
-
* - Row fields: flatten children (valueHandling: 'flatten')
|
|
672
|
-
* - Group fields: nest children under group key (valueHandling: 'include')
|
|
673
|
-
* - Text fields: exclude from values (valueHandling: 'exclude')
|
|
674
|
-
* - Value fields: include with their key and value type
|
|
675
751
|
*/
|
|
676
752
|
type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string, unknown> : T extends {
|
|
677
|
-
type: 'page';
|
|
678
|
-
fields: infer
|
|
679
|
-
} ?
|
|
680
|
-
type: 'row';
|
|
681
|
-
fields: infer TFields;
|
|
682
|
-
} ? TFields extends RegisteredFieldTypes[] ? InferFormValueWithDepth<TFields, Depth[D]> : never : T extends {
|
|
753
|
+
type: 'page' | 'row';
|
|
754
|
+
fields: infer F;
|
|
755
|
+
} ? F extends RegisteredFieldTypes[] ? InferFormValueWithDepth<F, Depth[D]> : never : T extends {
|
|
683
756
|
type: 'group';
|
|
684
757
|
key: infer K;
|
|
685
|
-
fields: infer
|
|
686
|
-
} ? K extends string ?
|
|
687
|
-
[P in K]: InferFormValueWithDepth<
|
|
758
|
+
fields: infer F;
|
|
759
|
+
} ? K extends string ? F extends RegisteredFieldTypes[] ? {
|
|
760
|
+
[P in K]: InferFormValueWithDepth<F, Depth[D]>;
|
|
688
761
|
} : {
|
|
689
762
|
[P in K]: Record<string, unknown>;
|
|
690
763
|
} : never : T extends {
|
|
@@ -692,39 +765,46 @@ type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string
|
|
|
692
765
|
} ? never : T extends {
|
|
693
766
|
key: infer K;
|
|
694
767
|
value: infer V;
|
|
695
|
-
required: true;
|
|
696
|
-
} ? K extends string ? {
|
|
697
|
-
[P in K]: V;
|
|
698
|
-
} : never : T extends {
|
|
699
|
-
key: infer K;
|
|
700
|
-
value: infer V;
|
|
701
768
|
} ? K extends string ? {
|
|
702
|
-
[P in K]:
|
|
769
|
+
[P in K]: MaybeOptional<T, InferValueType<T, V>>;
|
|
703
770
|
} : never : never;
|
|
704
771
|
/**
|
|
705
772
|
* Internal helper with depth tracking
|
|
706
773
|
*/
|
|
707
774
|
type InferFormValueWithDepth<T extends RegisteredFieldTypes[], D extends number = 5> = UnionToIntersection<ProcessField<T[number], D>>;
|
|
708
775
|
/**
|
|
709
|
-
*
|
|
710
|
-
|
|
711
|
-
|
|
776
|
+
* Helper to extract fields from either fields array or FormConfig
|
|
777
|
+
*/
|
|
778
|
+
type ExtractFields<T> = T extends {
|
|
779
|
+
fields: infer TFields;
|
|
780
|
+
} ? TFields extends readonly RegisteredFieldTypes[] ? TFields : never : T extends readonly RegisteredFieldTypes[] ? T : never;
|
|
781
|
+
/**
|
|
782
|
+
* Infer form value type from fields array or FormConfig.
|
|
783
|
+
* Recursively processes nested structures and merges the results.
|
|
784
|
+
* Limited to 5 levels of nesting to prevent infinite type instantiation.
|
|
712
785
|
*
|
|
713
786
|
* @example
|
|
714
787
|
* ```typescript
|
|
788
|
+
* // From fields array
|
|
715
789
|
* const fields = [
|
|
716
790
|
* { type: 'input', key: 'email', value: '', required: true },
|
|
717
|
-
* { type: '
|
|
718
|
-
* { type: 'input', key: 'street', value: '' },
|
|
719
|
-
* { type: 'input', key: 'city', value: '' }
|
|
720
|
-
* ]}
|
|
791
|
+
* { type: 'input', key: 'name', value: '' }
|
|
721
792
|
* ] as const;
|
|
793
|
+
* type FieldsValue = InferFormValue<typeof fields>;
|
|
794
|
+
*
|
|
795
|
+
* // From FormConfig
|
|
796
|
+
* const config = {
|
|
797
|
+
* fields: [
|
|
798
|
+
* { type: 'input', key: 'email', value: '', required: true },
|
|
799
|
+
* { type: 'input', key: 'name', value: '' }
|
|
800
|
+
* ]
|
|
801
|
+
* } as const satisfies FormConfig;
|
|
802
|
+
* type ConfigValue = InferFormValue<typeof config>;
|
|
722
803
|
*
|
|
723
|
-
*
|
|
724
|
-
* // Result: { email: string; address: { street?: string; city?: string } }
|
|
804
|
+
* // Result: { email: string; name?: string }
|
|
725
805
|
* ```
|
|
726
806
|
*/
|
|
727
|
-
type InferFormValue$1<T extends RegisteredFieldTypes[]
|
|
807
|
+
type InferFormValue$1<T> = ExtractFields<T> extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<ExtractFields<T> extends RegisteredFieldTypes[] ? ExtractFields<T> : [...ExtractFields<T>], 5> : never;
|
|
728
808
|
|
|
729
809
|
/**
|
|
730
810
|
* Base configuration shared by all validators
|
|
@@ -1290,19 +1370,18 @@ interface SubmissionConfig<TValue = unknown> {
|
|
|
1290
1370
|
*
|
|
1291
1371
|
* @example
|
|
1292
1372
|
* ```typescript
|
|
1293
|
-
* const formConfig
|
|
1373
|
+
* const formConfig = {
|
|
1294
1374
|
* fields: [
|
|
1295
|
-
* { type: 'input', key: 'email', value: '', label: 'Email', required: true
|
|
1375
|
+
* { type: 'input', key: 'email', value: '', label: 'Email', required: true },
|
|
1296
1376
|
* { type: 'group', key: 'address', label: 'Address', fields: [
|
|
1297
1377
|
* { type: 'input', key: 'street', value: '', label: 'Street' },
|
|
1298
1378
|
* { type: 'input', key: 'city', value: '', label: 'City' }
|
|
1299
1379
|
* ]},
|
|
1300
|
-
* { type: 'button', key: 'submit', label: 'Submit', buttonType: 'submit' }
|
|
1301
1380
|
* ],
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
1304
|
-
*
|
|
1305
|
-
*
|
|
1381
|
+
* } as const satisfies FormConfig;
|
|
1382
|
+
*
|
|
1383
|
+
* // Infer form value type from config
|
|
1384
|
+
* type FormValue = InferFormValue<typeof formConfig>;
|
|
1306
1385
|
* ```
|
|
1307
1386
|
*
|
|
1308
1387
|
* @typeParam TFields - Array of registered field types available for this form
|
|
@@ -1310,7 +1389,7 @@ interface SubmissionConfig<TValue = unknown> {
|
|
|
1310
1389
|
*
|
|
1311
1390
|
* @public
|
|
1312
1391
|
*/
|
|
1313
|
-
interface FormConfig<TFields extends RegisteredFieldTypes[] = RegisteredFieldTypes[], TValue = InferFormValue$1<TFields>> {
|
|
1392
|
+
interface FormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = RegisteredFieldTypes[], TValue = InferFormValue$1<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>> {
|
|
1314
1393
|
/**
|
|
1315
1394
|
* Array of field definitions that define the form structure.
|
|
1316
1395
|
*
|
|
@@ -2151,11 +2230,6 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
2151
2230
|
readonly disabled: Signal<boolean>;
|
|
2152
2231
|
/** Whether the form is currently submitting. */
|
|
2153
2232
|
readonly submitting: Signal<boolean>;
|
|
2154
|
-
/**
|
|
2155
|
-
* Announcement message for screen readers.
|
|
2156
|
-
* Updates when form submission fails due to validation errors.
|
|
2157
|
-
*/
|
|
2158
|
-
readonly errorAnnouncement: _angular_core.WritableSignal<string>;
|
|
2159
2233
|
readonly initialized$: rxjs.Observable<boolean>;
|
|
2160
2234
|
/** Emits when form validity changes. */
|
|
2161
2235
|
readonly validityChange: _angular_core.OutputRef<boolean>;
|
|
@@ -2182,18 +2256,7 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
2182
2256
|
/** Emits when page navigation state changes (canGoNext, canGoPrevious, etc.). */
|
|
2183
2257
|
readonly onPageNavigationStateChange: _angular_core.OutputRef<PageNavigationStateChangeEvent>;
|
|
2184
2258
|
protected readonly resolvedFields: Signal<ResolvedField[]>;
|
|
2185
|
-
|
|
2186
|
-
private readonly clearErrorsOnConfigChange;
|
|
2187
|
-
private readonly emitPagedFormInitialized;
|
|
2188
|
-
private readonly emitNonPagedFormInitialized;
|
|
2189
|
-
private readonly handleFormReset;
|
|
2190
|
-
private readonly handleFormClear;
|
|
2191
|
-
/**
|
|
2192
|
-
* Announces form validation errors to screen readers when submission is attempted
|
|
2193
|
-
* on an invalid form.
|
|
2194
|
-
*/
|
|
2195
|
-
private readonly handleErrorAnnouncement;
|
|
2196
|
-
private readonly handleSubmission;
|
|
2259
|
+
constructor();
|
|
2197
2260
|
/**
|
|
2198
2261
|
* Handles native form submission triggered by:
|
|
2199
2262
|
* - Pressing Enter in an input field
|
|
@@ -2206,16 +2269,13 @@ declare class DynamicForm<TFields extends RegisteredFieldTypes[] = RegisteredFie
|
|
|
2206
2269
|
* @param event - The native submit event from the form element
|
|
2207
2270
|
*/
|
|
2208
2271
|
protected onNativeSubmit(event: Event): void;
|
|
2272
|
+
private setupEffects;
|
|
2273
|
+
private setupEventHandlers;
|
|
2209
2274
|
private registerValidatorsFromConfig;
|
|
2210
2275
|
private createFormSetupFromConfig;
|
|
2211
2276
|
private createEmptyFormSetup;
|
|
2212
2277
|
private onFormReset;
|
|
2213
2278
|
private onFormClear;
|
|
2214
|
-
/**
|
|
2215
|
-
* Counts the total number of validation errors across all fields.
|
|
2216
|
-
* Used for screen reader announcements.
|
|
2217
|
-
*/
|
|
2218
|
-
private countErrors;
|
|
2219
2279
|
ngOnDestroy(): void;
|
|
2220
2280
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicForm<any, any>, never>;
|
|
2221
2281
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicForm<any, any>, "form[dynamic-form]", never, { "config": { "alias": "dynamic-form"; "required": true; "isSignal": true; }; "formOptions": { "alias": "formOptions"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; "reset": "reset"; "cleared": "cleared"; "events": "events"; "initialized": "initialized"; "onPageChange": "onPageChange"; "onPageNavigationStateChange": "onPageNavigationStateChange"; }, never, never, true, never>;
|
|
@@ -2528,51 +2588,48 @@ interface InputProps<T extends InputType = InputType> {
|
|
|
2528
2588
|
type?: T;
|
|
2529
2589
|
placeholder?: DynamicText;
|
|
2530
2590
|
}
|
|
2591
|
+
/**
|
|
2592
|
+
* Input types that produce string values
|
|
2593
|
+
*/
|
|
2594
|
+
type StringInputType = Exclude<InputType, 'number'>;
|
|
2531
2595
|
/**
|
|
2532
2596
|
* Number input field with strict number value type.
|
|
2533
2597
|
* When props.type is 'number', value must be number.
|
|
2598
|
+
* Props is REQUIRED and must include type: 'number'.
|
|
2534
2599
|
*/
|
|
2535
2600
|
interface NumberInputField<TProps extends {
|
|
2536
2601
|
type?: string;
|
|
2537
|
-
} = {
|
|
2602
|
+
} = InputProps> extends BaseValueField<TProps & {
|
|
2538
2603
|
type: 'number';
|
|
2539
|
-
|
|
2540
|
-
}> extends BaseValueField<TProps, number> {
|
|
2604
|
+
}, number> {
|
|
2541
2605
|
type: 'input';
|
|
2542
|
-
props
|
|
2606
|
+
props: TProps & {
|
|
2607
|
+
type: 'number';
|
|
2608
|
+
};
|
|
2543
2609
|
}
|
|
2544
2610
|
/**
|
|
2545
2611
|
* String input field with strict string value type.
|
|
2546
|
-
* When props.type is text/email/etc, value must be string.
|
|
2612
|
+
* When props.type is text/email/etc (or undefined), value must be string.
|
|
2613
|
+
* Props type cannot be 'number'.
|
|
2547
2614
|
*/
|
|
2548
2615
|
interface StringInputField<TProps extends {
|
|
2549
2616
|
type?: string;
|
|
2550
|
-
} = {
|
|
2551
|
-
type
|
|
2552
|
-
|
|
2553
|
-
}> extends BaseValueField<TProps, string> {
|
|
2617
|
+
} = InputProps> extends BaseValueField<TProps & {
|
|
2618
|
+
type?: StringInputType;
|
|
2619
|
+
}, string> {
|
|
2554
2620
|
type: 'input';
|
|
2555
|
-
props?: TProps
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
* Helper type to extract the input type from props
|
|
2559
|
-
*/
|
|
2560
|
-
type ExtractInputType<TProps> = TProps extends {
|
|
2561
|
-
type?: infer T;
|
|
2562
|
-
} ? T : InputType;
|
|
2563
|
-
/**
|
|
2564
|
-
* Fallback variant when props is undefined - allows both string and number values
|
|
2565
|
-
*/
|
|
2566
|
-
interface UndefinedPropsInputField extends BaseValueField<undefined, string | number> {
|
|
2567
|
-
type: 'input';
|
|
2568
|
-
props?: never;
|
|
2621
|
+
props?: TProps & {
|
|
2622
|
+
type?: StringInputType;
|
|
2623
|
+
};
|
|
2569
2624
|
}
|
|
2570
2625
|
/**
|
|
2571
|
-
* Builds discriminated union
|
|
2626
|
+
* Builds discriminated union where props.type determines value type.
|
|
2627
|
+
* - If props.type is 'number', only NumberInputField matches (value: number)
|
|
2628
|
+
* - If props.type is undefined/text/email/etc, only StringInputField matches (value: string)
|
|
2572
2629
|
*/
|
|
2573
2630
|
type BuildInputFieldUnion<TProps extends {
|
|
2574
2631
|
type?: string;
|
|
2575
|
-
}> =
|
|
2632
|
+
}> = NumberInputField<TProps> | StringInputField<TProps>;
|
|
2576
2633
|
/**
|
|
2577
2634
|
* Input field with automatic type-safe value inference.
|
|
2578
2635
|
* TypeScript automatically infers the correct value type based on props.type.
|
|
@@ -3180,7 +3237,8 @@ declare class RowFieldComponent {
|
|
|
3180
3237
|
private readonly rawFieldRegistry;
|
|
3181
3238
|
private readonly fieldsSource;
|
|
3182
3239
|
protected readonly resolvedFields: _angular_core.Signal<ResolvedField[]>;
|
|
3183
|
-
|
|
3240
|
+
constructor();
|
|
3241
|
+
private setupEffects;
|
|
3184
3242
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<RowFieldComponent, never>;
|
|
3185
3243
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<RowFieldComponent, "div[row-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
3186
3244
|
}
|
|
@@ -3227,7 +3285,8 @@ declare class GroupFieldComponent<TModel = Record<string, unknown>> {
|
|
|
3227
3285
|
readonly submitted: _angular_core.OutputRef<SubmitEvent>;
|
|
3228
3286
|
private readonly fieldsSource;
|
|
3229
3287
|
protected readonly resolvedFields: _angular_core.Signal<ResolvedField[]>;
|
|
3230
|
-
|
|
3288
|
+
constructor();
|
|
3289
|
+
private setupEffects;
|
|
3231
3290
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<GroupFieldComponent<any>, never>;
|
|
3232
3291
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<GroupFieldComponent<any>, "fieldset[group-field]", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "key": { "alias": "key"; "required": true; "isSignal": true; }; }, { "validityChange": "validityChange"; "dirtyChange": "dirtyChange"; "submitted": "submitted"; }, never, never, true, never>;
|
|
3233
3292
|
}
|
|
@@ -3270,9 +3329,9 @@ declare class ArrayFieldComponent<TModel = Record<string, unknown>> {
|
|
|
3270
3329
|
private readonly updateVersion;
|
|
3271
3330
|
private readonly itemOrderSignal;
|
|
3272
3331
|
readonly resolvedItems: _angular_core.WritableSignal<ResolvedArrayItem[]>;
|
|
3273
|
-
|
|
3274
|
-
private
|
|
3275
|
-
private
|
|
3332
|
+
constructor();
|
|
3333
|
+
private setupEffects;
|
|
3334
|
+
private setupEventHandlers;
|
|
3276
3335
|
private performDifferentialUpdate;
|
|
3277
3336
|
private resolveAllItems;
|
|
3278
3337
|
private appendItems;
|
|
@@ -3423,4 +3482,4 @@ declare function injectFieldRegistry(): {
|
|
|
3423
3482
|
};
|
|
3424
3483
|
|
|
3425
3484
|
export { ARRAY_CONTEXT, AddArrayItemEvent, ArrayFieldComponent, BUILT_IN_FIELDS, DynamicForm, DynamicTextPipe, EventBus, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FormClearEvent, FormResetEvent, GroupFieldComponent, NextPageEvent, PageChangeEvent, PreviousPageEvent, RemoveArrayItemEvent, RowFieldComponent, SubmitEvent, applyValidator, applyValidators, arrayFieldMapper, baseFieldMapper, buildBaseInputs, checkboxFieldMapper, createResolvedErrorsSignal, datepickerFieldMapper, dynamicTextToObservable, getChildFieldTree, getChildrenMap, getFieldProxy, getFieldValueHandling, groupFieldMapper, hasFormStructure, injectFieldRegistry, isArrayField, isCheckedField, isContainerField, isDisplayOnlyField, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isValueBearingField, isValueField, optionsFieldMapper, pageFieldMapper, provideDynamicForm, resolveNextButtonDisabled, resolveSubmitButtonDisabled, resolveTokens, rowFieldMapper, shouldShowErrors, valueFieldMapper };
|
|
3426
|
-
export type { ArrayAllowedChildren, ArrayContext, ArrayField, ArrayItemContext, AsyncCustomValidator, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonField, ButtonLogicContext, CheckboxField, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DatepickerField, DatepickerProps, DynamicFormFieldRegistry, DynamicText, EvaluationContext, EventArgs, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormInternals, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCustomValidator, HttpResourceRequest, HttpValidatorConfig, InferFormValue$1 as InferFormValue, InputField, InputProps, LeafFieldTypes, LogicConfig, MapperFn, MultiCheckboxField, NextButtonOptions, PageAllowedChildren, PageField, Prettify, RadioField, RegisteredFieldTypes, ResolvedError, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SelectField, SelectProps, SliderField, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TextareaField, TextareaProps, ToggleField, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };
|
|
3485
|
+
export type { ArrayAllowedChildren, ArrayContext, ArrayField, ArrayItemContext, AsyncCustomValidator, AsyncValidatorConfig, AvailableFieldTypes, BaseCheckedField, BaseValidatorConfig, BaseValueField, BuiltInValidatorConfig, ButtonField, ButtonLogicContext, CheckboxField, CheckedFieldComponent, ConditionalExpression, ContainerFieldTypes, CustomFnConfig, CustomValidator, CustomValidatorConfig, DatepickerField, DatepickerProps, DynamicFormFieldRegistry, DynamicText, EvaluationContext, EventArgs, ExtractField, ExtractFieldDefs, ExtractFormValue, FieldComponent, FieldDef, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldSignalContext, FieldTypeDefinition, FieldWithValidation, FormConfig, FormEvent, FormEventConstructor, FormInternals, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, GroupAllowedChildren, GroupField, HttpCustomValidator, HttpResourceRequest, HttpValidatorConfig, InferFormValue$1 as InferFormValue, InputField, InputProps, LeafFieldTypes, LogicConfig, MapperFn, MultiCheckboxField, NarrowField, NarrowFields, NextButtonOptions, PageAllowedChildren, PageField, Prettify, RadioField, RegisteredFieldTypes, ResolvedError, RowAllowedChildren, RowField, SchemaApplicationConfig, SchemaDefinition, SelectField, SelectProps, SliderField, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TextElementType, TextField, TextProps, TextareaField, TextareaProps, ToggleField, TokenContext, ValidationError, ValidationMessages, ValidatorConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WithInputSignals };
|