ng-form-foundry 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +72 -38
- package/fesm2022/ng-form-foundry.mjs +822 -0
- package/fesm2022/ng-form-foundry.mjs.map +1 -0
- package/index.d.ts +307 -0
- package/package.json +42 -5
- package/ng-package.json +0 -7
- package/src/lib/core/dynamic-recursive-forms-builder.ts +0 -129
- package/src/lib/core/utils.ts +0 -33
- package/src/lib/dynamic-recursive-form/anon-leaf-renderer/anon-leaf-renderer.component.html +0 -31
- package/src/lib/dynamic-recursive-form/anon-leaf-renderer/anon-leaf-renderer.component.scss +0 -11
- package/src/lib/dynamic-recursive-form/anon-leaf-renderer/anon-leaf-renderer.component.spec.ts +0 -22
- package/src/lib/dynamic-recursive-form/anon-leaf-renderer/anon-leaf-renderer.component.ts +0 -39
- package/src/lib/dynamic-recursive-form/dynamic-recursive-form.component.html +0 -168
- package/src/lib/dynamic-recursive-form/dynamic-recursive-form.component.scss +0 -128
- package/src/lib/dynamic-recursive-form/dynamic-recursive-form.component.spec.ts +0 -23
- package/src/lib/dynamic-recursive-form/dynamic-recursive-form.component.ts +0 -71
- package/src/lib/dynamic-recursive-form/dynamic-recursive-form.stories.ts +0 -36
- package/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.html +0 -8
- package/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.scss +0 -9
- package/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.spec.ts +0 -22
- package/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.ts +0 -21
- package/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.html +0 -31
- package/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.scss +0 -57
- package/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.spec.ts +0 -23
- package/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.ts +0 -52
- package/src/lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component.html +0 -17
- package/src/lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component.scss +0 -11
- package/src/lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component.spec.ts +0 -22
- package/src/lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component.ts +0 -41
- package/src/lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component.html +0 -24
- package/src/lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component.scss +0 -48
- package/src/lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component.spec.ts +0 -22
- package/src/lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component.ts +0 -101
- package/src/lib/types/dynamic-recursive.types.ts +0 -97
- package/src/lib/types/examples/complex-oai.ts +0 -938
- package/src/public-api.ts +0 -12
- package/tsconfig.lib.json +0 -18
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-form-foundry.mjs","sources":["../../../projects/ng-form-foundry/src/lib/types/dynamic-recursive.types.ts","../../../projects/ng-form-foundry/src/lib/core/dynamic-recursive-forms-builder.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.html","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component.html","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component.html","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/anon-leaf-renderer/anon-leaf-renderer.component.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/anon-leaf-renderer/anon-leaf-renderer.component.html","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.html","../../../projects/ng-form-foundry/src/lib/core/utils.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/dynamic-recursive-form.component.ts","../../../projects/ng-form-foundry/src/lib/dynamic-recursive-form/dynamic-recursive-form.component.html","../../../projects/ng-form-foundry/src/lib/config-editor/config-editor.component.ts","../../../projects/ng-form-foundry/src/lib/config-editor/config-editor.component.html","../../../projects/ng-form-foundry/src/public-api.ts","../../../projects/ng-form-foundry/src/ng-form-foundry.ts"],"sourcesContent":["import { FormArray, FormControl, FormGroup } from '@angular/forms';\n\nexport type LeafRuntimeType<T> = T extends 'string'\n ? string\n : T extends 'number'\n ? number\n : T extends 'boolean'\n ? boolean\n : T extends 'enum'\n ? string | number\n : never;\n\nexport type LeafBase = {\n kind: 'leaf';\n name: string;\n required?: true | undefined;\n label?: string;\n description?: string;\n};\n\nexport type AnonLeaf = {\n [K in Leaf['type']]: { type: K };\n}[Leaf['type']];\n\nexport type LeafString = LeafBase & {\n type: 'string';\n default?: LeafRuntimeType<'string'>;\n};\nexport type LeafNumber = LeafBase & {\n type: 'number';\n default?: LeafRuntimeType<'number'>;\n};\nexport type LeafBoolean = LeafBase & {\n type: 'boolean';\n default?: LeafRuntimeType<'boolean'>;\n};\nexport type LeafEnum = LeafBase & {\n type: 'enum';\n default?: LeafRuntimeType<'enum'>;\n enumLabel?: string[];\n enum: LeafRuntimeType<'enum'>[];\n};\n\nexport type Appearance = {\n flatten?: boolean;\n noBorder?: boolean;\n}\n\nexport type Leaf = LeafString | LeafNumber | LeafBoolean | LeafEnum;\n\nexport type LeafList<TKind extends Leaf['type'] = Leaf['type']> = {\n kind: 'leafList';\n label?: string;\n name: string;\n description?: string;\n default?: Exclude<Leaf['default'], undefined>[];\n type: TKind;\n minItems?: number;\n maxItems?: number;\n};\n\nexport type NodeGroupList = {\n kind: 'nodeGroupList';\n name: string;\n label?: string;\n description?: string;\n type: NodeGroup;\n minItems?: number;\n maxItems?: number;\n};\n\nexport type NodeGroup = {\n kind: 'nodeGroup';\n name: string;\n subType?: string;\n label?: string;\n root?: boolean;\n /**\n * When true, the group is optional: rendered with an on/off toggle and present\n * in the form only while enabled. Its control is removed from the parent\n * FormGroup when absent (so it drops from `form.value`) and re-added when the\n * user toggles it on. The builder omits it unless an initial value is supplied.\n */\n presence?: boolean;\n description?: string;\n children: Record<string, NodeType>;\n appearance?: Appearance;\n};\n\n/**\n * A discriminated selection: the user picks one `case`, and only that case's\n * fields are present. In the form it is a FormGroup holding a `__case` control\n * (the active case name) plus that case's field controls; switching the case\n * swaps the field controls.\n */\nexport type NodeChoice = {\n kind: 'choice';\n name: string;\n label?: string;\n cases: Record<string, Record<string, NodeType>>;\n default?: string;\n mandatory?: boolean;\n};\n\n/** The control name that records which case of a {@link NodeChoice} is active. */\nexport const CASE_KEY = '__case';\n\nexport type NodeType = Leaf | LeafList | NodeGroup | NodeGroupList | NodeChoice;\nexport type DFormControl<T extends NodeType> = T extends Leaf\n ? FormControl<LeafRuntimeType<T['type']>>\n : T extends LeafList\n ? FormArray<FormControl<LeafRuntimeType<T['type']>>>\n : T extends NodeGroup\n ? DFormGroup<T>\n : T extends NodeGroupList\n ? FormArray<DFormGroup<T['type']>>\n : T extends NodeChoice\n ? FormGroup<any>\n : never;\n\nexport type FormGroupType<T extends NodeGroup> = {\n [TChild in keyof T['children']]: DFormControl<T['children'][TChild]>;\n};\nexport type DFormGroup<T extends NodeGroup> = FormGroup<FormGroupType<T>>;\n","import {\n FormArray,\n FormControl,\n FormGroup,\n ValidatorFn,\n Validators,\n} from '@angular/forms';\nimport {\n CASE_KEY,\n DFormControl,\n DFormGroup,\n FormGroupType,\n Leaf,\n LeafEnum,\n LeafList,\n LeafRuntimeType,\n NodeChoice,\n NodeGroup,\n NodeGroupList,\n NodeType,\n} from '../types/dynamic-recursive.types';\n\n// --- type guards\nfunction isLeaf(node: NodeType): node is Leaf {\n return node.kind === 'leaf';\n}\n\nfunction isLeafList(node: NodeType): node is LeafList {\n return node.kind === 'leafList';\n}\nfunction isNodeGroup(node: NodeType): node is NodeGroup {\n return node.kind === 'nodeGroup';\n}\nfunction isNodeGroupList(node: NodeType): node is NodeGroupList {\n return node.kind === 'nodeGroupList';\n}\nfunction isChoice(node: NodeType): node is NodeChoice {\n return node.kind === 'choice';\n}\n\nfunction enumValidator(choices: readonly (string | number)[]): ValidatorFn {\n const set = new Set(choices);\n return (ctrl) =>\n ctrl.value == null || set.has(ctrl.value) ? null : { enum: true };\n}\n\nfunction buildLeafControl<L extends Leaf>(\n leaf: L,\n initial?: unknown,\n): FormControl<LeafRuntimeType<L['type']>> {\n const validators: ValidatorFn[] = [];\n if ('required' in leaf && leaf.required) validators.push(Validators.required);\n if ('type' in leaf && leaf.type === 'enum') {\n const choices = (leaf as LeafEnum).enum as (string | number)[];\n validators.push(enumValidator(choices));\n }\n const defaultValue =\n initial ?? ('default' in leaf ? (leaf as any).default : undefined) ?? null;\n return new FormControl<LeafRuntimeType<L['type']>>(defaultValue, {\n nonNullable: true,\n validators,\n });\n}\n\nfunction buildLeafListControl<L extends LeafList>(\n list: L,\n initial: LeafRuntimeType<L['type']>[] | null,\n): FormArray<FormControl<LeafRuntimeType<LeafList['type']> | null>> {\n const values = (Array.isArray(initial) ? initial : undefined) ??\n list.default ?? [null];\n return new FormArray(values.map((v) => new FormControl(v)));\n}\n\nfunction buildNodeGroupControl<G extends NodeGroup>(\n group: G,\n initial?: Record<string, unknown> | null,\n): DFormGroup<G> {\n const controls: any = {} as Partial<FormGroupType<G>>;\n for (const key in group.children) {\n const child = group.children[key];\n // Forward only this child's slice of the initial data, keyed by the child's\n // record key. Passing the whole `initial` object seeds every leaf with the\n // parent record and prevents list builders from sizing to the real data.\n controls[key] = buildControl(\n child,\n initial?.[key],\n ) as FormGroupType<G>[typeof key];\n }\n return new FormGroup(controls as FormGroupType<G>) as DFormGroup<G>;\n}\n\nfunction buildNodeGroupListControl<GL extends NodeGroupList>(\n list: GL,\n initial: unknown[] | null = null,\n): FormArray<DFormGroup<GL['type']>> {\n // `initial` is the runtime data array — one group per element. Fall back to a\n // single empty group only when no initial data is supplied.\n const values = Array.isArray(initial) ? initial : [null];\n return new FormArray(\n values.map((v) =>\n buildNodeGroupControl(list.type, v as Record<string, unknown> | null),\n ),\n );\n}\n\n/**\n * Build the `AbstractControl` for a single schema node.\n *\n * Dispatches on `node.kind`: a `leaf` becomes a `FormControl`, a `leafList` a\n * `FormArray` of controls, a `nodeGroup` a nested `FormGroup`, and a\n * `nodeGroupList` a `FormArray` of groups. `initial` is the runtime value for\n * this node — a scalar for a leaf, an array for a list, an object for a group —\n * and seeds the control's value (falling back to the node's `default`).\n *\n * Most callers use {@link buildFormFromSchema}; this is exposed for building a\n * control from a single non-root node.\n */\n/**\n * Build the FormGroup for a choice: a `__case` control holding the active case\n * name plus that case's field controls. Only the active case's fields are built,\n * matching the inline YANG encoding; switching the case swaps them.\n */\nfunction buildChoiceControl(\n choice: NodeChoice,\n initial?: Record<string, unknown> | null,\n): FormGroup {\n const active = (initial?.[CASE_KEY] as string | undefined) ?? choice.default;\n const controls: any = { [CASE_KEY]: new FormControl(active ?? null) };\n if (active && choice.cases[active]) {\n const caseChildren = choice.cases[active];\n for (const key in caseChildren) {\n controls[key] = buildControl(caseChildren[key], initial?.[key]);\n }\n }\n return new FormGroup(controls);\n}\n\nexport function buildControl<T extends NodeType>(\n node: T,\n initial?: unknown | null,\n): DFormControl<T> | FormControl<LeafRuntimeType<any>> | FormArray<any> {\n if (isLeaf(node)) {\n return buildLeafControl(node, initial);\n }\n if (isChoice(node)) {\n return buildChoiceControl(\n node,\n initial ? (initial as Record<string, unknown>) : null,\n ) as DFormControl<T>;\n }\n if (isLeafList(node)) {\n return buildLeafListControl(\n node,\n initial !== null\n ? (initial as LeafRuntimeType<(T & LeafList)['type']>[])\n : initial,\n ) as DFormControl<T>;\n }\n if (isNodeGroup(node)) {\n return buildNodeGroupControl(\n node,\n initial ? (initial as Record<string, unknown>) : null,\n ) as DFormControl<T>;\n }\n if (isNodeGroupList(node)) {\n return buildNodeGroupListControl(\n node,\n initial ? (initial as unknown[]) : null,\n ) as DFormControl<T>;\n }\n return new FormControl(initial ?? '') as DFormControl<T>;\n}\n\n/**\n * Build a typed `FormGroup` from a root `NodeGroup` schema.\n *\n * The returned group's control structure, keys, and value types are inferred\n * from the schema literal — a `leaf` of `type: 'number'` yields a\n * `FormControl<number>`, a `nodeGroup` a nested `FormGroup`, and so on. `initial`\n * is an optional value object keyed by the schema's `children` keys; each child\n * control is seeded from its matching slice (falling back to the node `default`).\n *\n * Inference only holds when `schema`'s literal type is preserved. Author schemas\n * with {@link defineSchema} or a `satisfies NodeGroup` annotation — never\n * `const schema: NodeGroup = ...`, which widens `children` and erases the field\n * names and value types.\n */\n/**\n * Remove presence groups that have no initial value so they are absent from the\n * built form (and from `form.value`) until the user toggles them on. Runs on the\n * fully-built, attached tree. `removeControl` is used rather than `disable`\n * because a disabled group is still included in `FormGroup.value`.\n */\nfunction applyPresence(\n group: FormGroup,\n schema: NodeGroup,\n initial?: Record<string, unknown> | null,\n): void {\n for (const key in schema.children) {\n const child = schema.children[key];\n if (child.kind !== 'nodeGroup') continue;\n const childInitial = (initial as Record<string, unknown> | null | undefined)?.[key];\n if (child.presence && childInitial == null) {\n group.removeControl(key);\n } else if (group.get(key) instanceof FormGroup) {\n applyPresence(group.get(key) as FormGroup, child, childInitial as Record<string, unknown> | null);\n }\n }\n}\n\nexport function buildFormFromSchema<S extends NodeGroup>(\n schema: S,\n initial: Record<string, unknown> | null = null,\n): DFormGroup<S> {\n const group = buildNodeGroupControl<S>(schema, initial);\n applyPresence(group, schema, initial);\n return group;\n}\n\n/**\n * Capture a schema literal with its exact type while checking it against\n * `NodeGroup`.\n *\n * This is an identity function whose only job is the `const` type parameter,\n * which keeps the narrow literal type of `schema` (field names, each node's\n * `type`) instead of widening it to `NodeGroup`. Assigning a schema to a\n * `: NodeGroup`-annotated constant widens `children` to\n * `Record<string, NodeType>` and erases that information, so\n * {@link buildFormFromSchema} can no longer infer a typed `FormGroup`. Passing\n * the schema through `defineSchema` (or annotating it `satisfies NodeGroup`)\n * preserves the schema-to-`FormGroup` inference.\n */\nexport function defineSchema<const S extends NodeGroup>(schema: S): S {\n return schema;\n}\n","import { Component, Input } from '@angular/core';\nimport { LeafEnum } from '../../types/dynamic-recursive.types';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatSelectModule } from '@angular/material/select';\n\n@Component({\n selector: 'nff-leaf-enum-renderer',\n standalone: true,\n imports: [ReactiveFormsModule, MatFormFieldModule, MatSelectModule],\n templateUrl: './leaf-enum-renderer.component.html',\n styleUrl: './leaf-enum-renderer.component.scss',\n})\nexport class LeafEnumRendererComponent {\n @Input() leafEnum!: LeafEnum;\n @Input() initialValue!: string;\n @Input() control = new FormControl();\n @Input() removable: boolean = false;\n @Input() remove: any;\n @Input() editable: boolean = true;\n}\n","<mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ leafEnum.label ?? leafEnum.name }}</mat-label>\n <mat-select [formControl]=\"control\" value=\"{{ control.value }}\">\n @for (option of leafEnum.enum; track $index) {\n <mat-option [value]=\"option\">{{ leafEnum.enumLabel?.[$index] ?? option }}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n","import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';\nimport { AnonLeaf, Leaf } from '../../types/dynamic-recursive.types';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { MatSelectModule } from '@angular/material/select';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatButtonModule } from '@angular/material/button';\nimport { LeafEnumRendererComponent } from '../leaf-enum-renderer/leaf-enum-renderer.component';\n\n@Component({\n selector: 'nff-leaf-renderer',\n standalone: true,\n imports: [\n MatFormFieldModule,\n ReactiveFormsModule,\n MatInputModule,\n MatCheckboxModule,\n MatSelectModule,\n MatIconModule,\n MatButtonModule,\n LeafEnumRendererComponent,\n ],\n templateUrl: './leaf-renderer.component.html',\n styleUrl: './leaf-renderer.component.scss',\n})\nexport class LeafRendererComponent implements OnInit {\n @Input() leaf_!: Leaf | AnonLeaf;\n @Input() control: FormControl = new FormControl();\n @Input() initialValue?: any;\n @Input() removable: boolean = false;\n @Input() editable = true;\n @Output() remove: EventEmitter<number> = new EventEmitter();\n\n ngOnInit(): void {\n if (this.initialValue) {\n this.control.patchValue(this.initialValue);\n }\n }\n}\n","@if ('name' in leaf_) {\n @if (leaf_.type === 'string') {\n <mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ leaf_.label ?? leaf_.name }}</mat-label>\n <input [readonly]=\"!editable\" matInput type=\"text\" [formControl]=\"control\">\n </mat-form-field>\n } @else if (leaf_.type === 'number') {\n <mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ leaf_.label ?? leaf_.name }}</mat-label>\n <input [readonly]=\"!editable\" matInput type=\"number\" [formControl]=\"control\">\n </mat-form-field>\n } @else if (leaf_.type === 'boolean') {\n <mat-checkbox [formControl]=\"control\">{{ leaf_.label ?? leaf_.name }}</mat-checkbox>\n } @else if (leaf_.type === 'enum') {\n <nff-leaf-enum-renderer [editable]=\"editable\" [leafEnum]=\"leaf_\" [control]=\"control\"></nff-leaf-enum-renderer>\n }\n}\n","import {\n AfterViewInit,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n forwardRef,\n inject,\n Input,\n OnInit,\n Output,\n QueryList,\n ViewChildren\n} from '@angular/core';\nimport { NodeGroupList } from '../../types/dynamic-recursive.types';\nimport { FormArray, FormGroup } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { DynamicRecursiveFormComponent } from '../dynamic-recursive-form.component';\nimport { buildFormFromSchema } from '../../core/dynamic-recursive-forms-builder';\nimport { MatTooltipModule } from '@angular/material/tooltip';\n\n@Component({\n selector: 'nff-node-group-list-renderer',\n standalone: true,\n imports: [\n forwardRef(() => DynamicRecursiveFormComponent),\n MatButtonModule,\n MatIconModule,\n MatTooltipModule,\n ],\n templateUrl: './node-group-list-renderer.component.html',\n styleUrl: './node-group-list-renderer.component.scss',\n})\nexport class NodeGroupListRendererComponent implements OnInit, AfterViewInit {\n @Input() nodeGroupList!: NodeGroupList;\n @Input() initialValue!: number[] | string[] | boolean[];\n @Input() formArray = new FormArray<any>([]);\n @Input() editable: boolean = true;\n @Input() minItems: number = 1;\n @Input() maxItems: number = 1;\n @Output() message = new EventEmitter();\n // forwardRef: DynamicRecursiveFormComponent and this component import each\n // other, so the class reference is undefined when this query is evaluated at\n // decoration time. forwardRef defers the lookup and keeps the selector valid.\n @ViewChildren(forwardRef(() => DynamicRecursiveFormComponent))\n items!: QueryList<DynamicRecursiveFormComponent>;\n\n cdr = inject(ChangeDetectorRef);\n\n ngOnInit() {\n if (this.initialValue) {\n this.formArray.patchValue(this.initialValue);\n }\n if (this.nodeGroupList.maxItems) {\n this.maxItems = this.nodeGroupList.maxItems;\n }\n if (this.nodeGroupList.minItems) {\n this.minItems = this.nodeGroupList.minItems;\n }\n }\n\n ngAfterViewInit() {\n this.items.changes.subscribe(() => {\n this.setLastEditable();\n });\n }\n\n removeItem($index: number) {\n if (this.formArray.length <= this.minItems) {\n this.message.emit({\n message: `You cannot remove the last ${this.nodeGroupList.type.name} configuration!`,\n type: 'error',\n })\n return;\n }\n this.formArray.removeAt($index);\n }\n\n addItem = (index: number) => {\n this.formArray.push(buildFormFromSchema(this.nodeGroupList.type, null));\n }\n\n setLastEditable() {\n const lastItem = this.items.last;\n if (lastItem) {\n lastItem.editable = true;\n }\n this.cdr.detectChanges();\n }\n\n asFormGroup(group: any) {\n return group as FormGroup;\n }\n\n getTitle($index: number) {\n let title = `${this.nodeGroupList.type.label ?? this.nodeGroupList.type.name}`;\n if (this.formArray.length > 1) {\n return `${title} #${$index + 1}`;\n }\n else {\n return title;\n }\n\n }\n}\n","@if (formArray && nodeGroupList) {\n <div class=\"fields-container\">\n @for (group of formArray.controls; track $index) {\n <div class=\"field-item\">\n <nff-dynamic-recursive-form\n [schema]=\"nodeGroupList.type\"\n [formGroup]=\"asFormGroup(group)\"\n [title]=\"getTitle($index)\"\n [index]=\"0\"\n [removable]=\"formArray.length > minItems\"\n (remove)=\"removeItem($index)\"\n [editable]=\"editable\"\n [addButtonCallback]=\"addItem\"\n />\n </div>\n }\n </div>\n}\n","import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { AnonLeaf } from '../../types/dynamic-recursive.types';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { MatSelectModule } from '@angular/material/select';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'nff-anon-leaf-renderer',\n standalone: true,\n imports: [\n ReactiveFormsModule,\n MatFormFieldModule,\n MatSelectModule,\n MatIconModule,\n MatCheckboxModule,\n MatInputModule,\n MatButtonModule,\n ],\n templateUrl: './anon-leaf-renderer.component.html',\n styleUrl: './anon-leaf-renderer.component.scss',\n})\nexport class AnonLeafRendererComponent {\n @Input() AnonLeaf!: AnonLeaf;\n @Input() initialValue!: string;\n @Input() control = new FormControl();\n @Input() removable: boolean = false;\n @Input() editable: boolean = true;\n @Input() index!: number;\n @Output() remove = new EventEmitter();\n @Input() label!: string;\n\n emitRemoveEvent() {\n this.remove.emit();\n }\n}\n","<div class=\"anon-leaf-container\">\n@if (AnonLeaf.type === 'string') {\n <mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ label }} #{{ index + 1}}</mat-label>\n <input [readonly]=\"!editable\"\n [formControl]=\"control\"\n matInput type=\"text\" >\n @if (removable) {\n }\n </mat-form-field>\n} @else if (AnonLeaf.type === 'number') {\n <mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ label }} #{{ index + 1}}</mat-label>\n <input [readonly]=\"!editable\"\n [formControl]=\"control\"\n matInput type=\"number\">\n </mat-form-field>\n} @else if (AnonLeaf.type === 'boolean') {\n <mat-checkbox [disabled]=\"!editable\" [formControl]=\"control\">value</mat-checkbox>\n}\n @else if (AnonLeaf.type === 'enum' && 'enum' in AnonLeaf) {\n <mat-form-field>\n <mat-label>{{ label }} #{{ index + 1}}</mat-label>\n <mat-select [formControl]=\"control\">\n @for (option of (AnonLeaf.type === 'enum' ? AnonLeaf.enum : []); track $index) {\n <mat-option [value]=\"option\">{{ option }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n}\n</div>\n","import { Component, EventEmitter, HostBinding, inject, Input, OnInit, Output } from '@angular/core';\nimport { LeafList } from '../../types/dynamic-recursive.types';\nimport { FormControl } from '@angular/forms';\nimport { MatIconModule } from '@angular/material/icon';\nimport { AnonLeafRendererComponent } from '../anon-leaf-renderer/anon-leaf-renderer.component';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatDialog } from '@angular/material/dialog';\nimport { MatPrefix } from '@angular/material/input';\n\n@Component({\n selector: 'nff-leaf-list-renderer',\n standalone: true,\n imports: [\n MatIconModule,\n AnonLeafRendererComponent,\n MatButtonModule,\n MatPrefix,\n ],\n templateUrl: './leaf-list-renderer.component.html',\n styleUrl: './leaf-list-renderer.component.scss',\n})\nexport class LeafListRendererComponent implements OnInit {\n @Input() leaf_!: LeafList;\n @Input() initialValue!: number[] | string[] | boolean[];\n @Input() formArray!: any;\n @Input() editable: boolean = true;\n @Input() minItems: number = 1;\n @Output() message = new EventEmitter();\n\n matDialog = inject(MatDialog);\n\n /**\n * Take a full-width row of its own once the array holds more than one entry.\n * A multi-entry array grows vertically as its items wrap; sitting inline next\n * to a regular leaf that would stretch the leaf to match. On its own line it\n * cannot. Consumed by the `:host(.stacked)` rule.\n */\n @HostBinding('class.stacked')\n get stacked(): boolean {\n return (this.formArray?.length ?? 0) > 1;\n }\n\n ngOnInit() {\n if (this.initialValue) {\n this.formArray.patchValue(this.initialValue);\n }\n }\n\n removeItem($index: number) {\n if (this.formArray.length <= this.minItems) {\n this.message.emit({\n message: 'You cannot remove the last item!',\n type: 'error',\n })\n return;\n }\n this.formArray.removeAt($index);\n }\n\n addItem() {\n this.formArray.push(new FormControl());\n }\n}\n","@if (formArray && leaf_) {\n @for (control of formArray.controls; track $index) {\n <div class=\"field-item\">\n <nff-anon-leaf-renderer\n [AnonLeaf]=\"leaf_\" [control]=\"control\"\n [removable]=\"minItems == null || formArray.length > minItems\"\n [index]=\"$index\"\n [editable]=\"editable\"\n [label]=\"leaf_.label ?? leaf_.name\"\n (remove)=\"removeItem($index)\"\n />\n @if (editable) {\n <div class=\"actions\">\n @if (formArray.length > minItems) {\n <button class=\"remove-button small-icon-button\" matIconButton (click)=\"removeItem($index)\">\n <mat-icon matPrefix>delete</mat-icon>\n </button>\n }\n @if ($last) {\n <button class=\"add-button small-icon-button\" matIconButton (click)=\"addItem()\">\n <mat-icon matPrefix>add</mat-icon>\n </button>\n }\n <div class=\"actions-spacer\"></div>\n </div>\n }\n </div>\n }\n}\n","import { FormArray, FormControl, FormGroup } from '@angular/forms';\nimport { Leaf, NodeGroup, NodeGroupList, NodeType } from '../types/dynamic-recursive.types';\n\nexport function nodeGroupChildrenList(schema: NodeGroup): Array<{ key: string; value: NodeType }> {\n const children = schema.children ?? {};\n return Object.entries(children).map(([key, value]) => ({\n key,\n value: value as NodeType,\n }));\n}\n\nexport function nodeGroupChildrenLeafs(schema: NodeGroup): Array<{ key: string; value: Leaf }> {\n const children = schema.children ?? {};\n return Object.entries(children).reduce((acc, [key, value]) => {\n if (value.kind === 'leaf') {\n acc.push({ key, value: value as Leaf });\n }\n return acc;\n }, [] as Array<{ key: string; value: Leaf }>);\n}\n\nexport function removeNullAndEmptyArrays(value: any): any {\n if (Array.isArray(value)) {\n const cleanedArray = value\n .map(removeNullAndEmptyArrays)\n .filter(v => v != null && (typeof v !== 'object' || Object.keys(v).length > 0));\n\n return cleanedArray.length > 0 ? cleanedArray : undefined;\n }\n\n if (value && typeof value === 'object') {\n const cleanedObject = Object.fromEntries(\n Object.entries(value)\n .map(([k, v]) => [k, removeNullAndEmptyArrays(v)])\n .filter(([_, v]) => v != null && (typeof v !== 'object' || Object.keys(v).length > 0))\n );\n\n return Object.keys(cleanedObject).length > 0 ? cleanedObject : undefined;\n }\n\n return value != null ? value : undefined;\n}\n\nexport function asFormControl(control: any) {\n return control as FormControl;\n}\n\nexport function asFormArray(control: any) {\n return control as FormArray;\n}\n\nexport function asFormGroup(control: any) {\n return control as FormGroup;\n}\n\n","import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';\nimport { LeafRendererComponent } from './leaf-renderer/leaf-renderer.component';\nimport { CASE_KEY, NodeChoice, NodeGroup, NodeType } from '../types/dynamic-recursive.types';\nimport {\n FormArray,\n FormControl,\n FormGroup,\n ReactiveFormsModule,\n} from '@angular/forms';\nimport { NodeGroupListRendererComponent } from './node-group-list-renderer/node-group-list-renderer.component';\nimport { LeafListRendererComponent } from './leaf-list-renderer/leaf-list-renderer.component';\nimport { MatExpansionModule } from '@angular/material/expansion';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatCardModule } from '@angular/material/card';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { asFormArray, asFormControl, asFormGroup } from '../core/utils';\nimport { buildControl, buildFormFromSchema } from '../core/dynamic-recursive-forms-builder';\nimport { MatTooltip } from '@angular/material/tooltip';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatSelectModule } from '@angular/material/select';\n\n@Component({\n imports: [\n LeafRendererComponent,\n NodeGroupListRendererComponent,\n LeafListRendererComponent,\n ReactiveFormsModule,\n MatExpansionModule,\n MatIconModule,\n MatButtonModule,\n NgTemplateOutlet,\n MatCardModule,\n MatCheckboxModule,\n MatFormFieldModule,\n MatSelectModule,\n MatTooltip,\n ],\n selector: 'nff-dynamic-recursive-form',\n standalone: true,\n styleUrl: './dynamic-recursive-form.component.scss',\n templateUrl: './dynamic-recursive-form.component.html',\n})\nexport class DynamicRecursiveFormComponent implements OnInit {\n @Input({ required: true }) schema!: NodeGroup;\n @Input() initialValue!: any;\n @Input() formGroup = new FormGroup<any>({});\n @Input() index: number | null = null;\n @Input() removable: boolean = false;\n @Output() remove = new EventEmitter();\n @Input() title!: string;\n @Input() editable = false;\n @Input() addButtonCallback: ((index: number) => void) | null = null;\n root: boolean = false;\n\n ngOnInit() {\n this.root = this.schema.root ?? false;\n if (this.initialValue) {\n this.formGroup.patchValue(this.initialValue);\n }\n }\n\n get nodeGroupChildrenList(): Array<{ key: string; value: NodeType }> {\n const children = this.schema.children ?? {};\n return Object.entries(children).map(([key, value]) => ({\n key,\n value: value as NodeType,\n }));\n }\n\n emitRemoveEvent() {\n this.remove.emit();\n }\n\n /**\n * Add or remove a presence group's control on this form. Removing it drops the\n * group from `form.value`; adding it rebuilds the sub-group from its schema.\n */\n togglePresence(key: string, schema: NodeGroup, present: boolean) {\n if (present) {\n if (!this.formGroup.get(key)) {\n this.formGroup.addControl(key, buildFormFromSchema(schema));\n }\n } else if (this.formGroup.get(key)) {\n this.formGroup.removeControl(key);\n }\n }\n\n protected readonly CASE_KEY = CASE_KEY;\n\n objectKeys(obj: Record<string, unknown>): string[] {\n return Object.keys(obj);\n }\n\n /** The active case name of a choice control, or null if none is selected. */\n activeCase(key: string): string | null {\n return (this.formGroup.get(key) as FormGroup | null)?.get(CASE_KEY)?.value ?? null;\n }\n\n /** A synthetic flattened NodeGroup used to render the active case's fields against the choice's FormGroup. */\n caseAsGroup(choice: NodeChoice, caseName: string): NodeGroup {\n return {\n kind: 'nodeGroup',\n name: choice.name,\n children: choice.cases[caseName] ?? {},\n appearance: { flatten: true },\n };\n }\n\n /** Swap a choice's field controls when the selected case changes. */\n switchCase(key: string, choice: NodeChoice, caseName: string) {\n const group = this.formGroup.get(key) as FormGroup;\n for (const name of Object.keys(group.controls)) {\n if (name !== CASE_KEY) group.removeControl(name);\n }\n const caseChildren = choice.cases[caseName] ?? {};\n for (const name in caseChildren) {\n group.addControl(name, buildControl(caseChildren[name]) as any);\n }\n }\n\n protected readonly asFormGroup = asFormGroup;\n protected readonly asFormArray = asFormArray;\n protected readonly asFormControl = asFormControl;\n}\n","@if (!root) {\n <ng-template #formRender>\n <div class=\"form-content\" [formGroup]=\"formGroup\">\n <div class=\"leafs-container\">\n <div class=\"fields\">\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'leaf') {\n <nff-leaf-renderer\n [leaf_]=child\n [control]=asFormControl(formGroup.get(child.name))\n [removable]=false\n [editable]=editable\n [initialValue]=\"formGroup.get(child.name)?.value\"\n />\n }\n }\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'leafList') {\n <nff-leaf-list-renderer\n [leaf_]=\"child\"\n [editable]=editable\n [formArray]=\"asFormArray(formGroup.get(child.name))\"\n [initialValue]=\"formGroup.get(child.name)?.value\"\n />\n }\n }\n </div>\n </div>\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'nodeGroup' && child.presence) {\n <div class=\"presence-group\">\n <mat-checkbox\n [checked]=\"!!formGroup.get(childItem.key)\"\n [disabled]=\"!editable\"\n (change)=\"togglePresence(childItem.key, child, $event.checked)\"\n >{{ child.label ?? child.name }}</mat-checkbox>\n @if (formGroup.get(childItem.key)) {\n <nff-dynamic-recursive-form\n [schema]=\"child\"\n [formGroup]=\"asFormGroup(formGroup.get(childItem.key))\"\n [editable]=\"editable\"\n />\n }\n </div>\n }\n @else if (child.kind == 'nodeGroup') {\n <nff-dynamic-recursive-form\n [schema]=\"child\"\n [formGroup]=\"asFormGroup(formGroup.get(child.name))\"\n [initialValue]=\"formGroup.get(child.name)?.value\"\n [editable]=\"editable\"\n />\n }\n @else if (child.kind == 'choice') {\n @let choiceGroup = asFormGroup(formGroup.get(childItem.key));\n @let selectedCase = activeCase(childItem.key);\n <div class=\"choice-group\" [formGroup]=\"choiceGroup\">\n <mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ child.label ?? child.name }}</mat-label>\n <mat-select\n [formControlName]=\"CASE_KEY\"\n (selectionChange)=\"switchCase(childItem.key, child, $event.value)\"\n >\n @for (caseName of objectKeys(child.cases); track caseName) {\n <mat-option [value]=\"caseName\">{{ caseName }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (selectedCase) {\n <nff-dynamic-recursive-form\n [schema]=\"caseAsGroup(child, selectedCase)\"\n [formGroup]=\"choiceGroup\"\n [editable]=\"editable\"\n />\n }\n </div>\n }\n }\n\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'nodeGroupList') {\n <nff-node-group-list-renderer\n [nodeGroupList]=\"child\"\n [formArray]=\"asFormArray(formGroup.get(child.name))\"\n [initialValue]=\"formGroup.get(child.name)?.value\"\n [editable]=\"editable\"\n />\n }\n }\n </div>\n </ng-template>\n @if (schema.appearance?.flatten) {\n <div class=\"flattened-form\">\n <ng-container *ngTemplateOutlet=\"formRender\"></ng-container>\n <div class=\"flattened-actions\">\n @if (editable && index != null && addButtonCallback != null) {\n <button matIconButton matTooltip=\"+ Add new {{ schema.label ?? schema.name }}\" class=\"add-button small-icon-button\" (click)=\"addButtonCallback(index)\">\n <mat-icon>add</mat-icon>\n </button>\n }\n @if (editable && removable) {\n <button matIconButton class=\"remove-button small-icon-button\" (click)=\"emitRemoveEvent(); $event.stopPropagation()\">\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n </div>\n }\n @else {\n <mat-card appearance=\"outlined\" [class]=\"{ 'mat-card-no-broder': schema.appearance?.noBorder }\">\n <mat-card-header>\n <div class=\"card-actions\">\n <button matIconButton class=\"small-icon-button\" [class]=\"{ 'edit-icon': !editable}\" (click)=\"editable = !editable\">\n <mat-icon>edit</mat-icon>\n </button>\n @if (editable && index != null && addButtonCallback != null) {\n <button matIconButton matTooltip=\"+ Add new {{ schema.label ?? schema.name }}\" class=\"add-button small-icon-button\" (click)=\"addButtonCallback(index)\">\n <mat-icon>add</mat-icon>\n </button>\n }\n @if (editable && removable) {\n <button matIconButton class=\"remove-button small-icon-button\" (click)=\"emitRemoveEvent(); $event.stopPropagation()\">\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n <mat-card-title class=\"config-form-subsection-card\">\n {{ title ?? schema.label ?? schema.name }}\n </mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <ng-container *ngTemplateOutlet=\"formRender\"></ng-container>\n </mat-card-content>\n </mat-card>\n }\n} @else {\n <div class=\"form-content\" [formGroup]=\"formGroup\">\n <div class=\"leafs-container\">\n <div class=\"fields\">\n <button matIconButton class=\"small-icon-button\" [class]=\"{ 'edit-icon': !editable }\" (click)=\"editable = !editable\">\n <mat-icon>edit</mat-icon>\n </button>\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'leaf') {\n <nff-leaf-renderer\n [leaf_]=child\n [control]=asFormControl(formGroup.get(child.name))\n [removable]=false\n [editable]=editable\n [initialValue]=\"formGroup.get(child.name)?.value\"\n />\n }\n }\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'leafList') {\n <nff-leaf-list-renderer\n [leaf_]=\"child\"\n [editable]=editable\n [formArray]=\"asFormArray(formGroup.get(child.name))\"\n [initialValue]=\"formGroup.get(child.name)?.value\"\n />\n }\n }\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'choice') {\n @let choiceGroup = asFormGroup(formGroup.get(childItem.key));\n @let selectedCase = activeCase(childItem.key);\n <div class=\"choice-group\" [formGroup]=\"choiceGroup\">\n <mat-form-field [appearance]=\"editable ? 'fill' : 'outline'\">\n <mat-label>{{ child.label ?? child.name }}</mat-label>\n <mat-select\n [formControlName]=\"CASE_KEY\"\n (selectionChange)=\"switchCase(childItem.key, child, $event.value)\"\n >\n @for (caseName of objectKeys(child.cases); track caseName) {\n <mat-option [value]=\"caseName\">{{ caseName }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (selectedCase) {\n <nff-dynamic-recursive-form\n [schema]=\"caseAsGroup(child, selectedCase)\"\n [formGroup]=\"choiceGroup\"\n [editable]=\"editable\"\n />\n }\n </div>\n }\n }\n </div>\n </div>\n <mat-accordion multi>\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'nodeGroupList') {\n <mat-expansion-panel togglePosition=\"before\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n {{ title ?? child.label ?? child.name }}\n </mat-panel-title>\n </mat-expansion-panel-header>\n <nff-node-group-list-renderer\n [nodeGroupList]=\"child\"\n [formArray]=\"asFormArray(formGroup.get(child.name))\"\n [initialValue]=\"formGroup.get(child.name)?.value\"\n [editable]=\"editable\"\n />\n </mat-expansion-panel>\n }\n }\n @for (childItem of nodeGroupChildrenList; track $index) {\n @let child = childItem.value;\n @if (child.kind == 'nodeGroup' && child.presence) {\n <mat-expansion-panel togglePosition=\"before\" [expanded]=\"!!formGroup.get(childItem.key)\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-checkbox\n [checked]=\"!!formGroup.get(childItem.key)\"\n [disabled]=\"!editable\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"togglePresence(childItem.key, child, $event.checked)\"\n >{{ child.label ?? child.name }}</mat-checkbox>\n </mat-panel-title>\n </mat-expansion-panel-header>\n @if (formGroup.get(childItem.key)) {\n <nff-dynamic-recursive-form\n [schema]=\"child\"\n [formGroup]=\"asFormGroup(formGroup.get(childItem.key))\"\n [editable]=\"editable\"\n />\n }\n </mat-expansion-panel>\n }\n @else if (child.kind == 'nodeGroup') {\n <mat-expansion-panel togglePosition=\"before\" expanded=\"true\">\n <mat-expansion-panel-header >\n <mat-panel-title>\n {{ title ?? child.label ?? child.name }}\n </mat-panel-title>\n </mat-expansion-panel-header>\n <nff-dynamic-recursive-form\n [schema]=\"child\"\n [formGroup]=\"asFormGroup(formGroup.get(child.name))\"\n [initialValue]=\"formGroup.get(child.name)?.value\"\n [title]=\"child.name ?? 'General Settings'\"\n [editable]=\"editable\"\n />\n </mat-expansion-panel>\n }\n }\n </mat-accordion>\n </div>\n}\n","import { Component, Input, OnInit } from '@angular/core';\nimport { FormArray, FormGroup, ReactiveFormsModule } from '@angular/forms';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { MatTooltip } from '@angular/material/tooltip';\nimport { Leaf, LeafList, NodeGroup } from '../types/dynamic-recursive.types';\nimport { asFormArray, asFormControl } from '../core/utils';\nimport { buildFormFromSchema } from '../core/dynamic-recursive-forms-builder';\nimport { LeafRendererComponent } from '../dynamic-recursive-form/leaf-renderer/leaf-renderer.component';\nimport { LeafListRendererComponent } from '../dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component';\n\n/** Metadata on a list-container node that lets the tree add items to its FormArray. */\ninterface ListRef {\n array: FormArray;\n itemSchema: NodeGroup;\n itemLabel: string;\n minItems: number;\n}\n\n/** A navigable node in the config tree. Groups and list items are tree nodes; leaves are their detail. */\ninterface TreeNode {\n id: string;\n label: string;\n children: TreeNode[];\n /** Leaves editable when this node is selected. Empty for list-container nodes. */\n leaves: { key: string; node: Leaf }[];\n leafLists: { key: string; node: LeafList }[];\n /** The FormGroup holding this node's leaves, or null for a list-container node. */\n group: FormGroup | null;\n /** Present on a nodeGroupList node: lets a `+` add an item. */\n list?: ListRef;\n /** Present on a list-item node: the FormArray and current index it can be removed from. */\n removable?: { array: FormArray; index: number };\n /** Present on an optional (presence) group node: lets a checkbox add/remove the group. */\n presence?: { parentGroup: FormGroup; key: string; schema: NodeGroup };\n /** For a presence node: whether the group is currently present. */\n present?: boolean;\n}\n\n/**\n * A tree/detail editor for a schema-built form: the structure (containers, lists,\n * groups) is a tree on the left, and selecting a node shows that node's leaf\n * fields for editing on the right. A `+` on a list node adds an entry; a delete\n * button on each item removes it. An alternative to the all-in-one\n * {@link DynamicRecursiveFormComponent} for large configs.\n */\n@Component({\n selector: 'nff-config-editor',\n standalone: true,\n imports: [\n ReactiveFormsModule,\n NgTemplateOutlet,\n MatIconModule,\n MatButtonModule,\n MatCheckboxModule,\n MatTooltip,\n LeafRendererComponent,\n LeafListRendererComponent,\n ],\n templateUrl: './config-editor.component.html',\n styleUrl: './config-editor.component.scss',\n})\nexport class ConfigEditorComponent implements OnInit {\n @Input({ required: true }) schema!: NodeGroup;\n @Input({ required: true }) formGroup!: FormGroup;\n @Input() editable = true;\n\n root!: TreeNode;\n selected: TreeNode | null = null;\n readonly expanded = new Set<string>();\n\n private nextId = 0;\n\n ngOnInit() {\n this.root = this.buildTree(this.schema, this.formGroup, this.schema.label ?? this.schema.name);\n this.expanded.add(this.root.id);\n this.select(this.root);\n }\n\n select(node: TreeNode) {\n this.selected = node;\n // Reveal the node's direct children in the tree as it opens on the right.\n if (node.children.length) this.expanded.add(node.id);\n }\n\n /** Select a child (list item or sub-group) from the detail pane, keeping its parent expanded in the tree. */\n open(parent: TreeNode, child: TreeNode) {\n this.expanded.add(parent.id);\n this.select(child);\n }\n\n /**\n * Root-to-`target` path for the detail-pane breadcrumb (inclusive of both ends),\n * or an empty array if `target` is not in the current tree. Searched fresh each\n * call so it stays correct after add/remove/presence mutations rebuild subtrees.\n */\n pathTo(target: TreeNode): TreeNode[] {\n const walk = (node: TreeNode, trail: TreeNode[]): TreeNode[] | null => {\n const here = [...trail, node];\n if (node === target) return here;\n for (const child of node.children) {\n const found = walk(child, here);\n if (found) return found;\n }\n return null;\n };\n return this.root ? (walk(this.root, []) ?? []) : [];\n }\n\n toggle(node: TreeNode) {\n if (this.expanded.has(node.id)) this.expanded.delete(node.id);\n else this.expanded.add(node.id);\n }\n\n /** Append a new item to a list node's FormArray and to the tree, then select it. */\n addItem(listNode: TreeNode) {\n const list = listNode.list;\n if (!list) return;\n const group = buildFormFromSchema(list.itemSchema);\n list.array.push(group);\n const item = this.buildTree(list.itemSchema, group, list.itemLabel);\n item.removable = { array: list.array, index: list.array.length - 1 };\n listNode.children.push(item);\n this.renumber(listNode);\n this.expanded.add(listNode.id);\n this.select(item);\n }\n\n /** Remove a list item from its FormArray and the tree (down to `minItems`). */\n removeItem(listNode: TreeNode, item: TreeNode) {\n if (!listNode.list || !item.removable) return;\n if (listNode.list.array.length <= listNode.list.minItems) return;\n listNode.list.array.removeAt(item.removable.index);\n listNode.children.splice(listNode.children.indexOf(item), 1);\n this.renumber(listNode);\n if (this.selected === item) this.select(listNode);\n }\n\n /** Enable or disable an optional (presence) group by adding/removing its control. */\n setPresence(node: TreeNode, present: boolean) {\n const p = node.presence;\n if (!p) return;\n if (present) {\n if (!(p.parentGroup.get(p.key) instanceof FormGroup)) {\n const group = buildFormFromSchema(p.schema);\n p.parentGroup.addControl(p.key, group);\n const built = this.buildTree(p.schema, group, node.label);\n node.children = built.children;\n node.leaves = built.leaves;\n node.leafLists = built.leafLists;\n node.group = group;\n }\n node.present = true;\n this.select(node);\n } else {\n p.parentGroup.removeControl(p.key);\n node.children = [];\n node.leaves = [];\n node.leafLists = [];\n node.group = null;\n node.present = false;\n }\n }\n\n protected readonly asFormControl = asFormControl;\n protected readonly asFormArray = asFormArray;\n\n private placeholder(label: string): TreeNode {\n return { id: String(this.nextId++), label, children: [], leaves: [], leafLists: [], group: null };\n }\n\n /** Re-index and re-label a list node's item children (just \"#n\") after add/remove. */\n private renumber(listNode: TreeNode): void {\n listNode.children.forEach((child, i) => {\n if (child.removable) child.removable.index = i;\n child.label = `#${i + 1}`;\n });\n }\n\n private buildTree(schema: NodeGroup, group: FormGroup, label: string): TreeNode {\n const leaves: TreeNode['leaves'] = [];\n const leafLists: TreeNode['leafLists'] = [];\n const children: TreeNode[] = [];\n\n for (const key of Object.keys(schema.children)) {\n const child = schema.children[key];\n if (child.kind === 'leaf') {\n leaves.push({ key, node: child });\n } else if (child.kind === 'leafList') {\n leafLists.push({ key, node: child });\n } else if (child.kind === 'nodeGroup') {\n const childGroup = group.get(key);\n if (child.presence) {\n // Optional group: always a tree node — a placeholder when absent.\n const node =\n childGroup instanceof FormGroup\n ? this.buildTree(child, childGroup, child.label ?? key)\n : this.placeholder(child.label ?? key);\n node.presence = { parentGroup: group, key, schema: child };\n node.present = childGroup instanceof FormGroup;\n children.push(node);\n } else if (childGroup instanceof FormGroup) {\n children.push(this.buildTree(child, childGroup, child.label ?? key));\n }\n } else if (child.kind === 'nodeGroupList') {\n const array = group.get(key);\n const itemLabel = child.type.label ?? child.type.name;\n const items =\n array instanceof FormArray\n ? array.controls\n .filter((c): c is FormGroup => c instanceof FormGroup)\n .map((item, i) => {\n // Just \"#n\": the item sits under its list node, so repeating the\n // item name (e.g. \"Interface #1\") only echoes the parent.\n const node = this.buildTree(child.type, item, `#${i + 1}`);\n node.removable = { array, index: i };\n return node;\n })\n : [];\n children.push({\n id: String(this.nextId++),\n label: child.label ?? key,\n children: items,\n leaves: [],\n leafLists: [],\n group: null,\n list:\n array instanceof FormArray\n ? { array, itemSchema: child.type, itemLabel, minItems: child.minItems ?? 0 }\n : undefined,\n });\n }\n // choice nodes are not shown in the tree yet.\n }\n\n return { id: String(this.nextId++), label, children, leaves, leafLists, group };\n }\n}\n","<div class=\"editor\">\n <nav class=\"tree\">\n <ng-template #treeNode let-node let-depth=\"depth\" let-parent=\"parent\">\n <div\n class=\"tree-row\"\n [class.selected]=\"node === selected\"\n [style.padding-left.px]=\"8 + depth * 16\"\n (click)=\"select(node)\"\n >\n @if (node.children.length) {\n <button\n matIconButton\n class=\"twisty\"\n (click)=\"toggle(node); $event.stopPropagation()\"\n [attr.aria-label]=\"expanded.has(node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon>{{ expanded.has(node.id) ? 'expand_more' : 'chevron_right' }}</mat-icon>\n </button>\n } @else {\n <span class=\"twisty-spacer\"></span>\n }\n\n @if (node.presence) {\n <mat-checkbox\n class=\"presence-check\"\n [checked]=\"!!node.present\"\n [disabled]=\"!editable\"\n (change)=\"setPresence(node, $event.checked)\"\n (click)=\"$event.stopPropagation()\"\n ></mat-checkbox>\n }\n <span class=\"tree-label\" [class.absent]=\"node.presence && !node.present\">{{ node.label }}</span>\n\n @if (editable && node.list) {\n <button\n matIconButton\n class=\"row-btn add\"\n [matTooltip]=\"'Add ' + node.list.itemLabel\"\n (click)=\"addItem(node); $event.stopPropagation()\"\n >\n <mat-icon>add</mat-icon>\n </button>\n }\n @if (editable && node.removable) {\n <button\n matIconButton\n class=\"row-btn remove\"\n matTooltip=\"Remove\"\n (click)=\"removeItem(parent, node); $event.stopPropagation()\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n\n @if (expanded.has(node.id)) {\n @for (child of node.children; track child.id) {\n <ng-container\n *ngTemplateOutlet=\"treeNode; context: { $implicit: child, depth: depth + 1, parent: node }\"\n />\n }\n }\n </ng-template>\n\n <ng-container *ngTemplateOutlet=\"treeNode; context: { $implicit: root, depth: 0 }\" />\n </nav>\n\n <section class=\"detail\">\n @if (selected) {\n <nav class=\"breadcrumb\" aria-label=\"Breadcrumb\">\n @for (crumb of pathTo(selected); track crumb.id; let last = $last) {\n @if (last) {\n <span class=\"crumb-current\" aria-current=\"page\">{{ crumb.label }}</span>\n } @else {\n <button type=\"button\" class=\"item-link crumb-link\" (click)=\"select(crumb)\">{{ crumb.label }}</button>\n <span class=\"crumb-sep\" aria-hidden=\"true\">/</span>\n }\n }\n </nav>\n }\n @if (selected && selected.group) {\n <div class=\"group-body\">\n <div class=\"fields\" [formGroup]=\"selected.group\">\n @for (leaf of selected.leaves; track leaf.key) {\n <nff-leaf-renderer\n [leaf_]=\"leaf.node\"\n [control]=\"asFormControl(selected.group.get(leaf.key))\"\n [editable]=\"editable\"\n />\n }\n @for (list of selected.leafLists; track list.key) {\n <nff-leaf-list-renderer\n [leaf_]=\"list.node\"\n [formArray]=\"asFormArray(selected.group.get(list.key))\"\n [editable]=\"editable\"\n />\n }\n @if (!selected.leaves.length && !selected.leafLists.length && !selected.children.length) {\n <p class=\"empty\">This node has no fields.</p>\n }\n </div>\n @if (selected.children.length) {\n <nav class=\"child-links\">\n <h4 class=\"child-links-title\">Sections</h4>\n @for (child of selected.children; track child.id) {\n <button type=\"button\" class=\"item-link child-link\" (click)=\"open(selected, child)\">\n <mat-icon>chevron_right</mat-icon>{{ child.label }}\n </button>\n }\n </nav>\n }\n </div>\n } @else if (selected && selected.presence && !selected.present) {\n <p class=\"empty\">This optional group is off. Tick its box in the tree to add it.</p>\n } @else if (selected && selected.list) {\n @if (selected.children.length) {\n <ul class=\"item-list\">\n @for (item of selected.children; track item.id) {\n <li class=\"item-row\">\n <button type=\"button\" class=\"item-link\" (click)=\"open(selected, item)\">{{ item.label }}</button>\n @if (editable && item.removable) {\n <button\n matIconButton\n class=\"row-btn remove-item\"\n matTooltip=\"Remove\"\n (click)=\"removeItem(selected, item)\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n }\n </li>\n }\n </ul>\n } @else {\n <p class=\"empty\">No {{ selected.list.itemLabel }} items yet.</p>\n }\n @if (editable) {\n <div class=\"list-actions\">\n <button matButton (click)=\"addItem(selected)\">\n <mat-icon>add</mat-icon> Add {{ selected.list.itemLabel }}\n </button>\n </div>\n }\n } @else {\n <p class=\"empty\">Select a node on the left to edit its fields.</p>\n }\n </section>\n</div>\n","/*\n * Public API Surface of ng-form-foundry\n */\n\nexport * from './lib/types/dynamic-recursive.types';\nexport * from './lib/core/dynamic-recursive-forms-builder';\nexport * from './lib/dynamic-recursive-form/dynamic-recursive-form.component';\nexport * from './lib/config-editor/config-editor.component';\nexport * from './lib/dynamic-recursive-form/leaf-renderer/leaf-renderer.component';\nexport * from './lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component';\nexport * from './lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component';\nexport * from './lib/dynamic-recursive-form/node-group-list-renderer/node-group-list-renderer.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1","i2","i3","i4","i6","i7"],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { FormControl, FormArray, FormGroup } from '@angular/forms';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { OnInit, EventEmitter, AfterViewInit, QueryList, ChangeDetectorRef } from '@angular/core';
|
|
4
|
+
import { MatDialog } from '@angular/material/dialog';
|
|
5
|
+
|
|
6
|
+
type LeafRuntimeType<T> = T extends 'string' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : T extends 'enum' ? string | number : never;
|
|
7
|
+
type LeafBase = {
|
|
8
|
+
kind: 'leaf';
|
|
9
|
+
name: string;
|
|
10
|
+
required?: true | undefined;
|
|
11
|
+
label?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
};
|
|
14
|
+
type AnonLeaf = {
|
|
15
|
+
[K in Leaf['type']]: {
|
|
16
|
+
type: K;
|
|
17
|
+
};
|
|
18
|
+
}[Leaf['type']];
|
|
19
|
+
type LeafString = LeafBase & {
|
|
20
|
+
type: 'string';
|
|
21
|
+
default?: LeafRuntimeType<'string'>;
|
|
22
|
+
};
|
|
23
|
+
type LeafNumber = LeafBase & {
|
|
24
|
+
type: 'number';
|
|
25
|
+
default?: LeafRuntimeType<'number'>;
|
|
26
|
+
};
|
|
27
|
+
type LeafBoolean = LeafBase & {
|
|
28
|
+
type: 'boolean';
|
|
29
|
+
default?: LeafRuntimeType<'boolean'>;
|
|
30
|
+
};
|
|
31
|
+
type LeafEnum = LeafBase & {
|
|
32
|
+
type: 'enum';
|
|
33
|
+
default?: LeafRuntimeType<'enum'>;
|
|
34
|
+
enumLabel?: string[];
|
|
35
|
+
enum: LeafRuntimeType<'enum'>[];
|
|
36
|
+
};
|
|
37
|
+
type Appearance = {
|
|
38
|
+
flatten?: boolean;
|
|
39
|
+
noBorder?: boolean;
|
|
40
|
+
};
|
|
41
|
+
type Leaf = LeafString | LeafNumber | LeafBoolean | LeafEnum;
|
|
42
|
+
type LeafList<TKind extends Leaf['type'] = Leaf['type']> = {
|
|
43
|
+
kind: 'leafList';
|
|
44
|
+
label?: string;
|
|
45
|
+
name: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
default?: Exclude<Leaf['default'], undefined>[];
|
|
48
|
+
type: TKind;
|
|
49
|
+
minItems?: number;
|
|
50
|
+
maxItems?: number;
|
|
51
|
+
};
|
|
52
|
+
type NodeGroupList = {
|
|
53
|
+
kind: 'nodeGroupList';
|
|
54
|
+
name: string;
|
|
55
|
+
label?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
type: NodeGroup;
|
|
58
|
+
minItems?: number;
|
|
59
|
+
maxItems?: number;
|
|
60
|
+
};
|
|
61
|
+
type NodeGroup = {
|
|
62
|
+
kind: 'nodeGroup';
|
|
63
|
+
name: string;
|
|
64
|
+
subType?: string;
|
|
65
|
+
label?: string;
|
|
66
|
+
root?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* When true, the group is optional: rendered with an on/off toggle and present
|
|
69
|
+
* in the form only while enabled. Its control is removed from the parent
|
|
70
|
+
* FormGroup when absent (so it drops from `form.value`) and re-added when the
|
|
71
|
+
* user toggles it on. The builder omits it unless an initial value is supplied.
|
|
72
|
+
*/
|
|
73
|
+
presence?: boolean;
|
|
74
|
+
description?: string;
|
|
75
|
+
children: Record<string, NodeType>;
|
|
76
|
+
appearance?: Appearance;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* A discriminated selection: the user picks one `case`, and only that case's
|
|
80
|
+
* fields are present. In the form it is a FormGroup holding a `__case` control
|
|
81
|
+
* (the active case name) plus that case's field controls; switching the case
|
|
82
|
+
* swaps the field controls.
|
|
83
|
+
*/
|
|
84
|
+
type NodeChoice = {
|
|
85
|
+
kind: 'choice';
|
|
86
|
+
name: string;
|
|
87
|
+
label?: string;
|
|
88
|
+
cases: Record<string, Record<string, NodeType>>;
|
|
89
|
+
default?: string;
|
|
90
|
+
mandatory?: boolean;
|
|
91
|
+
};
|
|
92
|
+
/** The control name that records which case of a {@link NodeChoice} is active. */
|
|
93
|
+
declare const CASE_KEY = "__case";
|
|
94
|
+
type NodeType = Leaf | LeafList | NodeGroup | NodeGroupList | NodeChoice;
|
|
95
|
+
type DFormControl<T extends NodeType> = T extends Leaf ? FormControl<LeafRuntimeType<T['type']>> : T extends LeafList ? FormArray<FormControl<LeafRuntimeType<T['type']>>> : T extends NodeGroup ? DFormGroup<T> : T extends NodeGroupList ? FormArray<DFormGroup<T['type']>> : T extends NodeChoice ? FormGroup<any> : never;
|
|
96
|
+
type FormGroupType<T extends NodeGroup> = {
|
|
97
|
+
[TChild in keyof T['children']]: DFormControl<T['children'][TChild]>;
|
|
98
|
+
};
|
|
99
|
+
type DFormGroup<T extends NodeGroup> = FormGroup<FormGroupType<T>>;
|
|
100
|
+
|
|
101
|
+
declare function buildControl<T extends NodeType>(node: T, initial?: unknown | null): DFormControl<T> | FormControl<LeafRuntimeType<any>> | FormArray<any>;
|
|
102
|
+
declare function buildFormFromSchema<S extends NodeGroup>(schema: S, initial?: Record<string, unknown> | null): DFormGroup<S>;
|
|
103
|
+
/**
|
|
104
|
+
* Capture a schema literal with its exact type while checking it against
|
|
105
|
+
* `NodeGroup`.
|
|
106
|
+
*
|
|
107
|
+
* This is an identity function whose only job is the `const` type parameter,
|
|
108
|
+
* which keeps the narrow literal type of `schema` (field names, each node's
|
|
109
|
+
* `type`) instead of widening it to `NodeGroup`. Assigning a schema to a
|
|
110
|
+
* `: NodeGroup`-annotated constant widens `children` to
|
|
111
|
+
* `Record<string, NodeType>` and erases that information, so
|
|
112
|
+
* {@link buildFormFromSchema} can no longer infer a typed `FormGroup`. Passing
|
|
113
|
+
* the schema through `defineSchema` (or annotating it `satisfies NodeGroup`)
|
|
114
|
+
* preserves the schema-to-`FormGroup` inference.
|
|
115
|
+
*/
|
|
116
|
+
declare function defineSchema<const S extends NodeGroup>(schema: S): S;
|
|
117
|
+
|
|
118
|
+
declare function asFormControl(control: any): FormControl;
|
|
119
|
+
declare function asFormArray(control: any): FormArray;
|
|
120
|
+
declare function asFormGroup(control: any): FormGroup;
|
|
121
|
+
|
|
122
|
+
declare class DynamicRecursiveFormComponent implements OnInit {
|
|
123
|
+
schema: NodeGroup;
|
|
124
|
+
initialValue: any;
|
|
125
|
+
formGroup: FormGroup<any>;
|
|
126
|
+
index: number | null;
|
|
127
|
+
removable: boolean;
|
|
128
|
+
remove: EventEmitter<any>;
|
|
129
|
+
title: string;
|
|
130
|
+
editable: boolean;
|
|
131
|
+
addButtonCallback: ((index: number) => void) | null;
|
|
132
|
+
root: boolean;
|
|
133
|
+
ngOnInit(): void;
|
|
134
|
+
get nodeGroupChildrenList(): Array<{
|
|
135
|
+
key: string;
|
|
136
|
+
value: NodeType;
|
|
137
|
+
}>;
|
|
138
|
+
emitRemoveEvent(): void;
|
|
139
|
+
/**
|
|
140
|
+
* Add or remove a presence group's control on this form. Removing it drops the
|
|
141
|
+
* group from `form.value`; adding it rebuilds the sub-group from its schema.
|
|
142
|
+
*/
|
|
143
|
+
togglePresence(key: string, schema: NodeGroup, present: boolean): void;
|
|
144
|
+
protected readonly CASE_KEY = "__case";
|
|
145
|
+
objectKeys(obj: Record<string, unknown>): string[];
|
|
146
|
+
/** The active case name of a choice control, or null if none is selected. */
|
|
147
|
+
activeCase(key: string): string | null;
|
|
148
|
+
/** A synthetic flattened NodeGroup used to render the active case's fields against the choice's FormGroup. */
|
|
149
|
+
caseAsGroup(choice: NodeChoice, caseName: string): NodeGroup;
|
|
150
|
+
/** Swap a choice's field controls when the selected case changes. */
|
|
151
|
+
switchCase(key: string, choice: NodeChoice, caseName: string): void;
|
|
152
|
+
protected readonly asFormGroup: typeof asFormGroup;
|
|
153
|
+
protected readonly asFormArray: typeof asFormArray;
|
|
154
|
+
protected readonly asFormControl: typeof asFormControl;
|
|
155
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DynamicRecursiveFormComponent, never>;
|
|
156
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<DynamicRecursiveFormComponent, "nff-dynamic-recursive-form", never, { "schema": { "alias": "schema"; "required": true; }; "initialValue": { "alias": "initialValue"; "required": false; }; "formGroup": { "alias": "formGroup"; "required": false; }; "index": { "alias": "index"; "required": false; }; "removable": { "alias": "removable"; "required": false; }; "title": { "alias": "title"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; "addButtonCallback": { "alias": "addButtonCallback"; "required": false; }; }, { "remove": "remove"; }, never, never, true, never>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Metadata on a list-container node that lets the tree add items to its FormArray. */
|
|
160
|
+
interface ListRef {
|
|
161
|
+
array: FormArray;
|
|
162
|
+
itemSchema: NodeGroup;
|
|
163
|
+
itemLabel: string;
|
|
164
|
+
minItems: number;
|
|
165
|
+
}
|
|
166
|
+
/** A navigable node in the config tree. Groups and list items are tree nodes; leaves are their detail. */
|
|
167
|
+
interface TreeNode {
|
|
168
|
+
id: string;
|
|
169
|
+
label: string;
|
|
170
|
+
children: TreeNode[];
|
|
171
|
+
/** Leaves editable when this node is selected. Empty for list-container nodes. */
|
|
172
|
+
leaves: {
|
|
173
|
+
key: string;
|
|
174
|
+
node: Leaf;
|
|
175
|
+
}[];
|
|
176
|
+
leafLists: {
|
|
177
|
+
key: string;
|
|
178
|
+
node: LeafList;
|
|
179
|
+
}[];
|
|
180
|
+
/** The FormGroup holding this node's leaves, or null for a list-container node. */
|
|
181
|
+
group: FormGroup | null;
|
|
182
|
+
/** Present on a nodeGroupList node: lets a `+` add an item. */
|
|
183
|
+
list?: ListRef;
|
|
184
|
+
/** Present on a list-item node: the FormArray and current index it can be removed from. */
|
|
185
|
+
removable?: {
|
|
186
|
+
array: FormArray;
|
|
187
|
+
index: number;
|
|
188
|
+
};
|
|
189
|
+
/** Present on an optional (presence) group node: lets a checkbox add/remove the group. */
|
|
190
|
+
presence?: {
|
|
191
|
+
parentGroup: FormGroup;
|
|
192
|
+
key: string;
|
|
193
|
+
schema: NodeGroup;
|
|
194
|
+
};
|
|
195
|
+
/** For a presence node: whether the group is currently present. */
|
|
196
|
+
present?: boolean;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* A tree/detail editor for a schema-built form: the structure (containers, lists,
|
|
200
|
+
* groups) is a tree on the left, and selecting a node shows that node's leaf
|
|
201
|
+
* fields for editing on the right. A `+` on a list node adds an entry; a delete
|
|
202
|
+
* button on each item removes it. An alternative to the all-in-one
|
|
203
|
+
* {@link DynamicRecursiveFormComponent} for large configs.
|
|
204
|
+
*/
|
|
205
|
+
declare class ConfigEditorComponent implements OnInit {
|
|
206
|
+
schema: NodeGroup;
|
|
207
|
+
formGroup: FormGroup;
|
|
208
|
+
editable: boolean;
|
|
209
|
+
root: TreeNode;
|
|
210
|
+
selected: TreeNode | null;
|
|
211
|
+
readonly expanded: Set<string>;
|
|
212
|
+
private nextId;
|
|
213
|
+
ngOnInit(): void;
|
|
214
|
+
select(node: TreeNode): void;
|
|
215
|
+
/** Select a child (list item or sub-group) from the detail pane, keeping its parent expanded in the tree. */
|
|
216
|
+
open(parent: TreeNode, child: TreeNode): void;
|
|
217
|
+
/**
|
|
218
|
+
* Root-to-`target` path for the detail-pane breadcrumb (inclusive of both ends),
|
|
219
|
+
* or an empty array if `target` is not in the current tree. Searched fresh each
|
|
220
|
+
* call so it stays correct after add/remove/presence mutations rebuild subtrees.
|
|
221
|
+
*/
|
|
222
|
+
pathTo(target: TreeNode): TreeNode[];
|
|
223
|
+
toggle(node: TreeNode): void;
|
|
224
|
+
/** Append a new item to a list node's FormArray and to the tree, then select it. */
|
|
225
|
+
addItem(listNode: TreeNode): void;
|
|
226
|
+
/** Remove a list item from its FormArray and the tree (down to `minItems`). */
|
|
227
|
+
removeItem(listNode: TreeNode, item: TreeNode): void;
|
|
228
|
+
/** Enable or disable an optional (presence) group by adding/removing its control. */
|
|
229
|
+
setPresence(node: TreeNode, present: boolean): void;
|
|
230
|
+
protected readonly asFormControl: typeof asFormControl;
|
|
231
|
+
protected readonly asFormArray: typeof asFormArray;
|
|
232
|
+
private placeholder;
|
|
233
|
+
/** Re-index and re-label a list node's item children (just "#n") after add/remove. */
|
|
234
|
+
private renumber;
|
|
235
|
+
private buildTree;
|
|
236
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ConfigEditorComponent, never>;
|
|
237
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ConfigEditorComponent, "nff-config-editor", never, { "schema": { "alias": "schema"; "required": true; }; "formGroup": { "alias": "formGroup"; "required": true; }; "editable": { "alias": "editable"; "required": false; }; }, {}, never, never, true, never>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
declare class LeafRendererComponent implements OnInit {
|
|
241
|
+
leaf_: Leaf | AnonLeaf;
|
|
242
|
+
control: FormControl;
|
|
243
|
+
initialValue?: any;
|
|
244
|
+
removable: boolean;
|
|
245
|
+
editable: boolean;
|
|
246
|
+
remove: EventEmitter<number>;
|
|
247
|
+
ngOnInit(): void;
|
|
248
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LeafRendererComponent, never>;
|
|
249
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LeafRendererComponent, "nff-leaf-renderer", never, { "leaf_": { "alias": "leaf_"; "required": false; }; "control": { "alias": "control"; "required": false; }; "initialValue": { "alias": "initialValue"; "required": false; }; "removable": { "alias": "removable"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; }, { "remove": "remove"; }, never, never, true, never>;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
declare class LeafListRendererComponent implements OnInit {
|
|
253
|
+
leaf_: LeafList;
|
|
254
|
+
initialValue: number[] | string[] | boolean[];
|
|
255
|
+
formArray: any;
|
|
256
|
+
editable: boolean;
|
|
257
|
+
minItems: number;
|
|
258
|
+
message: EventEmitter<any>;
|
|
259
|
+
matDialog: MatDialog;
|
|
260
|
+
/**
|
|
261
|
+
* Take a full-width row of its own once the array holds more than one entry.
|
|
262
|
+
* A multi-entry array grows vertically as its items wrap; sitting inline next
|
|
263
|
+
* to a regular leaf that would stretch the leaf to match. On its own line it
|
|
264
|
+
* cannot. Consumed by the `:host(.stacked)` rule.
|
|
265
|
+
*/
|
|
266
|
+
get stacked(): boolean;
|
|
267
|
+
ngOnInit(): void;
|
|
268
|
+
removeItem($index: number): void;
|
|
269
|
+
addItem(): void;
|
|
270
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LeafListRendererComponent, never>;
|
|
271
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LeafListRendererComponent, "nff-leaf-list-renderer", never, { "leaf_": { "alias": "leaf_"; "required": false; }; "initialValue": { "alias": "initialValue"; "required": false; }; "formArray": { "alias": "formArray"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; "minItems": { "alias": "minItems"; "required": false; }; }, { "message": "message"; }, never, never, true, never>;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
declare class LeafEnumRendererComponent {
|
|
275
|
+
leafEnum: LeafEnum;
|
|
276
|
+
initialValue: string;
|
|
277
|
+
control: FormControl<any>;
|
|
278
|
+
removable: boolean;
|
|
279
|
+
remove: any;
|
|
280
|
+
editable: boolean;
|
|
281
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LeafEnumRendererComponent, never>;
|
|
282
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LeafEnumRendererComponent, "nff-leaf-enum-renderer", never, { "leafEnum": { "alias": "leafEnum"; "required": false; }; "initialValue": { "alias": "initialValue"; "required": false; }; "control": { "alias": "control"; "required": false; }; "removable": { "alias": "removable"; "required": false; }; "remove": { "alias": "remove"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; }, {}, never, never, true, never>;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
declare class NodeGroupListRendererComponent implements OnInit, AfterViewInit {
|
|
286
|
+
nodeGroupList: NodeGroupList;
|
|
287
|
+
initialValue: number[] | string[] | boolean[];
|
|
288
|
+
formArray: FormArray<any>;
|
|
289
|
+
editable: boolean;
|
|
290
|
+
minItems: number;
|
|
291
|
+
maxItems: number;
|
|
292
|
+
message: EventEmitter<any>;
|
|
293
|
+
items: QueryList<DynamicRecursiveFormComponent>;
|
|
294
|
+
cdr: ChangeDetectorRef;
|
|
295
|
+
ngOnInit(): void;
|
|
296
|
+
ngAfterViewInit(): void;
|
|
297
|
+
removeItem($index: number): void;
|
|
298
|
+
addItem: (index: number) => void;
|
|
299
|
+
setLastEditable(): void;
|
|
300
|
+
asFormGroup(group: any): FormGroup;
|
|
301
|
+
getTitle($index: number): string;
|
|
302
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NodeGroupListRendererComponent, never>;
|
|
303
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<NodeGroupListRendererComponent, "nff-node-group-list-renderer", never, { "nodeGroupList": { "alias": "nodeGroupList"; "required": false; }; "initialValue": { "alias": "initialValue"; "required": false; }; "formArray": { "alias": "formArray"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; "minItems": { "alias": "minItems"; "required": false; }; "maxItems": { "alias": "maxItems"; "required": false; }; }, { "message": "message"; }, never, never, true, never>;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export { CASE_KEY, ConfigEditorComponent, DynamicRecursiveFormComponent, LeafEnumRendererComponent, LeafListRendererComponent, LeafRendererComponent, NodeGroupListRendererComponent, buildControl, buildFormFromSchema, defineSchema };
|
|
307
|
+
export type { AnonLeaf, Appearance, DFormControl, DFormGroup, FormGroupType, Leaf, LeafBase, LeafBoolean, LeafEnum, LeafList, LeafNumber, LeafRuntimeType, LeafString, NodeChoice, NodeGroup, NodeGroupList, NodeType };
|
package/package.json
CHANGED
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ng-form-foundry",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Build fully-typed Angular Reactive Forms and Angular Material UI from a declarative, recursive form-description schema.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"angular",
|
|
7
|
+
"forms",
|
|
8
|
+
"reactive-forms",
|
|
9
|
+
"dynamic-forms",
|
|
10
|
+
"schema-driven-forms",
|
|
11
|
+
"form-builder",
|
|
12
|
+
"angular-material",
|
|
13
|
+
"json-schema",
|
|
14
|
+
"typescript"
|
|
15
|
+
],
|
|
16
|
+
"author": "Mathias Santos de Brito",
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/mathiasbrito/ng-form-foundry.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://ng-form-foundry.readthedocs.io",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/mathiasbrito/ng-form-foundry/issues"
|
|
25
|
+
},
|
|
4
26
|
"peerDependencies": {
|
|
5
|
-
"@angular/
|
|
6
|
-
"@angular/
|
|
27
|
+
"@angular/cdk": "^20.2.0",
|
|
28
|
+
"@angular/common": "^20.1.0",
|
|
29
|
+
"@angular/core": "^20.1.0",
|
|
30
|
+
"@angular/forms": "^20.1.0",
|
|
31
|
+
"@angular/material": "^20.2.0",
|
|
32
|
+
"rxjs": "^7.8.0"
|
|
7
33
|
},
|
|
8
34
|
"dependencies": {
|
|
9
35
|
"tslib": "^2.3.0"
|
|
10
36
|
},
|
|
11
|
-
"sideEffects": false
|
|
12
|
-
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"module": "fesm2022/ng-form-foundry.mjs",
|
|
39
|
+
"typings": "index.d.ts",
|
|
40
|
+
"exports": {
|
|
41
|
+
"./package.json": {
|
|
42
|
+
"default": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./index.d.ts",
|
|
46
|
+
"default": "./fesm2022/ng-form-foundry.mjs"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|