ng-form-foundry 0.0.1 → 0.2.1
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
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<mat-form-field [appearance]="editable ? 'fill' : 'outline'">
|
|
2
|
-
<mat-label>{{ leafEnum.label ?? leafEnum.name }}</mat-label>
|
|
3
|
-
<mat-select [formControl]="control" value="{{ control.value }}">
|
|
4
|
-
@for (option of leafEnum.enum; track $index) {
|
|
5
|
-
<mat-option [value]="option">{{ leafEnum.enumLabel?.[$index] ?? option }}</mat-option>
|
|
6
|
-
}
|
|
7
|
-
</mat-select>
|
|
8
|
-
</mat-form-field>
|
package/src/lib/dynamic-recursive-form/leaf-enum-renderer/leaf-enum-renderer.component.spec.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { LeafEnumRendererComponent } from './leaf-enum-renderer.component';
|
|
4
|
-
|
|
5
|
-
describe('DrfLeafEnumRendererComponent', () => {
|
|
6
|
-
let component: LeafEnumRendererComponent;
|
|
7
|
-
let fixture: ComponentFixture<LeafEnumRendererComponent>;
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
await TestBed.configureTestingModule({
|
|
11
|
-
imports: [LeafEnumRendererComponent],
|
|
12
|
-
}).compileComponents();
|
|
13
|
-
|
|
14
|
-
fixture = TestBed.createComponent(LeafEnumRendererComponent);
|
|
15
|
-
component = fixture.componentInstance;
|
|
16
|
-
fixture.detectChanges();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should create', () => {
|
|
20
|
-
expect(component).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
2
|
-
import { LeafEnum } from '../../types/dynamic-recursive.types';
|
|
3
|
-
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
4
|
-
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
5
|
-
import { MatSelectModule } from '@angular/material/select';
|
|
6
|
-
|
|
7
|
-
@Component({
|
|
8
|
-
selector: 'nff-leaf-enum-renderer',
|
|
9
|
-
standalone: true,
|
|
10
|
-
imports: [ReactiveFormsModule, MatFormFieldModule, MatSelectModule],
|
|
11
|
-
templateUrl: './leaf-enum-renderer.component.html',
|
|
12
|
-
styleUrl: './leaf-enum-renderer.component.scss',
|
|
13
|
-
})
|
|
14
|
-
export class LeafEnumRendererComponent {
|
|
15
|
-
@Input() leafEnum!: LeafEnum;
|
|
16
|
-
@Input() initialValue!: string;
|
|
17
|
-
@Input() control = new FormControl();
|
|
18
|
-
@Input() removable: boolean = false;
|
|
19
|
-
@Input() remove: any;
|
|
20
|
-
@Input() editable: boolean = true;
|
|
21
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
@if (formArray && leaf_) {
|
|
2
|
-
<div class="fields">
|
|
3
|
-
@for (control of formArray.controls; track $index) {
|
|
4
|
-
<div class="field-item">
|
|
5
|
-
<nff-anon-leaf-renderer
|
|
6
|
-
[AnonLeaf]="leaf_" [control]="control"
|
|
7
|
-
[removable]="minItems == null || formArray.length > minItems"
|
|
8
|
-
[index]="$index"
|
|
9
|
-
[editable]="editable"
|
|
10
|
-
[label]="leaf_.label ?? leaf_.name"
|
|
11
|
-
(remove)="removeItem($index)"
|
|
12
|
-
/>
|
|
13
|
-
<div class="actions">
|
|
14
|
-
@if (formArray.length > minItems && editable) {
|
|
15
|
-
<button class="remove-button small-icon-button" matIconButton (click)="removeItem($index)">
|
|
16
|
-
<mat-icon matPrefix>delete</mat-icon>
|
|
17
|
-
</button>
|
|
18
|
-
}
|
|
19
|
-
@if ($last) {
|
|
20
|
-
@if (editable) {
|
|
21
|
-
<button class="add-button small-icon-button" matIconButton (click)="addItem()">
|
|
22
|
-
<mat-icon matPrefix>add</mat-icon>
|
|
23
|
-
</button>
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
<div class="actions-spacer"></div>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
}
|
|
30
|
-
</div>
|
|
31
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
@use '@angular/material' as mat;
|
|
2
|
-
|
|
3
|
-
:host {
|
|
4
|
-
display: flex;
|
|
5
|
-
flex-direction: column;
|
|
6
|
-
flex-wrap: wrap;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.fields-container {
|
|
10
|
-
position: relative;
|
|
11
|
-
display: flex;
|
|
12
|
-
flex-wrap: wrap;
|
|
13
|
-
gap: 8px;
|
|
14
|
-
align-items: baseline;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.field-item {
|
|
18
|
-
display: flex;
|
|
19
|
-
flex-direction: row;
|
|
20
|
-
gap: 8px;
|
|
21
|
-
align-items: center;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.actions {
|
|
25
|
-
z-index: 100;
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-direction: column; /* stack buttons vertically */
|
|
28
|
-
align-items: flex-end;
|
|
29
|
-
gap: 4px;
|
|
30
|
-
height: 100%;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.actions-spacer {
|
|
34
|
-
height: 20px;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
nff-anon-leaf-renderer {
|
|
38
|
-
width: 100%;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.add-button {
|
|
42
|
-
background-color: var(--mat-sys-primary-container);
|
|
43
|
-
@include mat.elevation(1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.remove-button {
|
|
47
|
-
background-color: var(--mat-sys-error-container);
|
|
48
|
-
@include mat.elevation(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.title {
|
|
52
|
-
display: flex;
|
|
53
|
-
flex-direction: row;
|
|
54
|
-
gap: 8px;
|
|
55
|
-
align-items: center;
|
|
56
|
-
}
|
|
57
|
-
|
package/src/lib/dynamic-recursive-form/leaf-list-renderer/leaf-list-renderer.component.spec.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { LeafListRendererComponent } from './leaf-list-renderer.component';
|
|
4
|
-
|
|
5
|
-
describe('DrfLeafListRendererComponent', () => {
|
|
6
|
-
let component: LeafListRendererComponent;
|
|
7
|
-
let fixture: ComponentFixture<LeafListRendererComponent>;
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
await TestBed.configureTestingModule({
|
|
11
|
-
imports: [LeafListRendererComponent]
|
|
12
|
-
})
|
|
13
|
-
.compileComponents();
|
|
14
|
-
|
|
15
|
-
fixture = TestBed.createComponent(LeafListRendererComponent);
|
|
16
|
-
component = fixture.componentInstance;
|
|
17
|
-
fixture.detectChanges();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should create', () => {
|
|
21
|
-
expect(component).toBeTruthy();
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { Component, EventEmitter, inject, Input, OnInit, Output } from '@angular/core';
|
|
2
|
-
import { LeafList } from '../../types/dynamic-recursive.types';
|
|
3
|
-
import { FormControl } from '@angular/forms';
|
|
4
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
5
|
-
import { AnonLeafRendererComponent } from '../anon-leaf-renderer/anon-leaf-renderer.component';
|
|
6
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
7
|
-
import { MatDialog } from '@angular/material/dialog';
|
|
8
|
-
import { MatPrefix } from '@angular/material/input';
|
|
9
|
-
|
|
10
|
-
@Component({
|
|
11
|
-
selector: 'nff-leaf-list-renderer',
|
|
12
|
-
standalone: true,
|
|
13
|
-
imports: [
|
|
14
|
-
MatIconModule,
|
|
15
|
-
AnonLeafRendererComponent,
|
|
16
|
-
MatButtonModule,
|
|
17
|
-
MatPrefix,
|
|
18
|
-
],
|
|
19
|
-
templateUrl: './leaf-list-renderer.component.html',
|
|
20
|
-
styleUrl: './leaf-list-renderer.component.scss',
|
|
21
|
-
})
|
|
22
|
-
export class LeafListRendererComponent implements OnInit {
|
|
23
|
-
@Input() leaf_!: LeafList;
|
|
24
|
-
@Input() initialValue!: number[] | string[] | boolean[];
|
|
25
|
-
@Input() formArray!: any;
|
|
26
|
-
@Input() editable: boolean = true;
|
|
27
|
-
@Input() minItems: number = 1;
|
|
28
|
-
@Output() message = new EventEmitter();
|
|
29
|
-
|
|
30
|
-
matDialog = inject(MatDialog);
|
|
31
|
-
|
|
32
|
-
ngOnInit() {
|
|
33
|
-
if (this.initialValue) {
|
|
34
|
-
this.formArray.patchValue(this.initialValue);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
removeItem($index: number) {
|
|
39
|
-
if (this.formArray.length <= this.minItems) {
|
|
40
|
-
this.message.emit({
|
|
41
|
-
message: 'You cannot remove the last item!',
|
|
42
|
-
type: 'error',
|
|
43
|
-
})
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
this.formArray.removeAt($index);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
addItem() {
|
|
50
|
-
this.formArray.push(new FormControl());
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
@if ('name' in leaf_) {
|
|
2
|
-
@if (leaf_.type === 'string') {
|
|
3
|
-
<mat-form-field [appearance]="editable ? 'fill' : 'outline'">
|
|
4
|
-
<mat-label>{{ leaf_.label ?? leaf_.name }}</mat-label>
|
|
5
|
-
<input [readonly]="!editable" matInput type="text" [formControl]="control">
|
|
6
|
-
</mat-form-field>
|
|
7
|
-
} @else if (leaf_.type === 'number') {
|
|
8
|
-
<mat-form-field [appearance]="editable ? 'fill' : 'outline'">
|
|
9
|
-
<mat-label>{{ leaf_.label ?? leaf_.name }}</mat-label>
|
|
10
|
-
<input [readonly]="!editable" matInput type="number" [formControl]="control">
|
|
11
|
-
</mat-form-field>
|
|
12
|
-
} @else if (leaf_.type === 'boolean') {
|
|
13
|
-
<mat-checkbox [formControl]="control">{{ leaf_.label ?? leaf_.name }}</mat-checkbox>
|
|
14
|
-
} @else if (leaf_.type === 'enum') {
|
|
15
|
-
<nff-leaf-enum-renderer [editable]="editable" [leafEnum]="leaf_" [control]="control"></nff-leaf-enum-renderer>
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { LeafRendererComponent } from './leaf-renderer.component';
|
|
4
|
-
|
|
5
|
-
describe('DrfLeafRendererComponent', () => {
|
|
6
|
-
let component: LeafRendererComponent;
|
|
7
|
-
let fixture: ComponentFixture<LeafRendererComponent>;
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
await TestBed.configureTestingModule({
|
|
11
|
-
imports: [LeafRendererComponent],
|
|
12
|
-
}).compileComponents();
|
|
13
|
-
|
|
14
|
-
fixture = TestBed.createComponent(LeafRendererComponent);
|
|
15
|
-
component = fixture.componentInstance;
|
|
16
|
-
fixture.detectChanges();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should create', () => {
|
|
20
|
-
expect(component).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
|
2
|
-
import { AnonLeaf, Leaf } from '../../types/dynamic-recursive.types';
|
|
3
|
-
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
4
|
-
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
5
|
-
import { MatInputModule } from '@angular/material/input';
|
|
6
|
-
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
7
|
-
import { MatSelectModule } from '@angular/material/select';
|
|
8
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
9
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
10
|
-
import { LeafEnumRendererComponent } from '../leaf-enum-renderer/leaf-enum-renderer.component';
|
|
11
|
-
|
|
12
|
-
@Component({
|
|
13
|
-
selector: 'nff-leaf-renderer',
|
|
14
|
-
standalone: true,
|
|
15
|
-
imports: [
|
|
16
|
-
MatFormFieldModule,
|
|
17
|
-
ReactiveFormsModule,
|
|
18
|
-
MatInputModule,
|
|
19
|
-
MatCheckboxModule,
|
|
20
|
-
MatSelectModule,
|
|
21
|
-
MatIconModule,
|
|
22
|
-
MatButtonModule,
|
|
23
|
-
LeafEnumRendererComponent,
|
|
24
|
-
],
|
|
25
|
-
templateUrl: './leaf-renderer.component.html',
|
|
26
|
-
styleUrl: './leaf-renderer.component.scss',
|
|
27
|
-
})
|
|
28
|
-
export class LeafRendererComponent implements OnInit {
|
|
29
|
-
@Input() leaf_!: Leaf | AnonLeaf;
|
|
30
|
-
@Input() control: FormControl = new FormControl();
|
|
31
|
-
@Input() initialValue?: any;
|
|
32
|
-
@Input() removable: boolean = false;
|
|
33
|
-
@Input() editable = true;
|
|
34
|
-
@Output() remove: EventEmitter<number> = new EventEmitter();
|
|
35
|
-
|
|
36
|
-
ngOnInit(): void {
|
|
37
|
-
if (this.initialValue) {
|
|
38
|
-
this.control.patchValue(this.initialValue);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
@if (formArray && nodeGroupList) {
|
|
2
|
-
<div class="fields-container">
|
|
3
|
-
@for (group of formArray.controls; track $index) {
|
|
4
|
-
<div class="field-item">
|
|
5
|
-
<nff-dynamic-recursive-form
|
|
6
|
-
[schema]="nodeGroupList.type"
|
|
7
|
-
[formGroup]="asFormGroup(group)"
|
|
8
|
-
[title]="getTitle($index)"
|
|
9
|
-
[index]="$index"
|
|
10
|
-
[removable]="formArray.length > minItems"
|
|
11
|
-
(remove)="removeItem($index)"
|
|
12
|
-
[editable]="editable"
|
|
13
|
-
/>
|
|
14
|
-
<div class="actions">
|
|
15
|
-
<button matIconButton class="small-icon-button"
|
|
16
|
-
matTooltip=" + Add new {{ nodeGroupList.type.label ?? nodeGroupList.type.name }}"
|
|
17
|
-
(click)="addItem()">
|
|
18
|
-
<mat-icon>add</mat-icon>
|
|
19
|
-
</button>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
}
|
|
23
|
-
</div>
|
|
24
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
:host {
|
|
2
|
-
position: relative;
|
|
3
|
-
width: 100%;
|
|
4
|
-
flex: 1 1 0;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.fields {
|
|
8
|
-
display: flex;
|
|
9
|
-
flex-direction: column;
|
|
10
|
-
flex-wrap: wrap;
|
|
11
|
-
width: 100%;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.fields-container {
|
|
15
|
-
display: flex;
|
|
16
|
-
flex-direction: column;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.field-item {
|
|
20
|
-
display: flex;
|
|
21
|
-
flex-direction: row;
|
|
22
|
-
gap: 8px;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.actions {
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-direction: row;
|
|
28
|
-
gap: 8px;
|
|
29
|
-
align-items: center;
|
|
30
|
-
margin-top: 0;
|
|
31
|
-
padding: 0;
|
|
32
|
-
min-width: 24px;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.title {
|
|
36
|
-
display: flex;
|
|
37
|
-
flex-direction: column;
|
|
38
|
-
gap: 8px;
|
|
39
|
-
align-items: center;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
nff-dynamic-recursive-form {
|
|
43
|
-
flex: 1;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.add-button {
|
|
47
|
-
background-color: var(--mat-sys-primary-container);
|
|
48
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { NodeGroupListRendererComponent } from './node-group-list-renderer.component';
|
|
4
|
-
|
|
5
|
-
describe('DrfNodeListRendererComponent', () => {
|
|
6
|
-
let component: NodeGroupListRendererComponent;
|
|
7
|
-
let fixture: ComponentFixture<NodeGroupListRendererComponent>;
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
await TestBed.configureTestingModule({
|
|
11
|
-
imports: [NodeGroupListRendererComponent],
|
|
12
|
-
}).compileComponents();
|
|
13
|
-
|
|
14
|
-
fixture = TestBed.createComponent(NodeGroupListRendererComponent);
|
|
15
|
-
component = fixture.componentInstance;
|
|
16
|
-
fixture.detectChanges();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should create', () => {
|
|
20
|
-
expect(component).toBeTruthy();
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AfterViewInit,
|
|
3
|
-
ChangeDetectorRef,
|
|
4
|
-
Component,
|
|
5
|
-
EventEmitter,
|
|
6
|
-
forwardRef,
|
|
7
|
-
inject,
|
|
8
|
-
Input,
|
|
9
|
-
OnInit,
|
|
10
|
-
Output,
|
|
11
|
-
QueryList,
|
|
12
|
-
ViewChildren
|
|
13
|
-
} from '@angular/core';
|
|
14
|
-
import { NodeGroupList } from '../../types/dynamic-recursive.types';
|
|
15
|
-
import { FormArray, FormGroup } from '@angular/forms';
|
|
16
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
17
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
18
|
-
import { DynamicRecursiveFormComponent } from '../dynamic-recursive-form.component';
|
|
19
|
-
import { buildFormFromSchema } from '../../core/dynamic-recursive-forms-builder';
|
|
20
|
-
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
21
|
-
|
|
22
|
-
@Component({
|
|
23
|
-
selector: 'nff-node-group-list-renderer',
|
|
24
|
-
standalone: true,
|
|
25
|
-
imports: [
|
|
26
|
-
forwardRef(() => DynamicRecursiveFormComponent),
|
|
27
|
-
MatButtonModule,
|
|
28
|
-
MatIconModule,
|
|
29
|
-
MatTooltipModule,
|
|
30
|
-
],
|
|
31
|
-
templateUrl: './node-group-list-renderer.component.html',
|
|
32
|
-
styleUrl: './node-group-list-renderer.component.scss',
|
|
33
|
-
})
|
|
34
|
-
export class NodeGroupListRendererComponent implements OnInit, AfterViewInit {
|
|
35
|
-
@Input() nodeGroupList!: NodeGroupList;
|
|
36
|
-
@Input() initialValue!: number[] | string[] | boolean[];
|
|
37
|
-
@Input() formArray = new FormArray<any>([]);
|
|
38
|
-
@Input() editable: boolean = true;
|
|
39
|
-
@Input() minItems: number = 1;
|
|
40
|
-
@Input() maxItems: number = 1;
|
|
41
|
-
@Output() message = new EventEmitter();
|
|
42
|
-
@ViewChildren(DynamicRecursiveFormComponent) items!: QueryList<DynamicRecursiveFormComponent>;
|
|
43
|
-
|
|
44
|
-
cdr = inject(ChangeDetectorRef);
|
|
45
|
-
|
|
46
|
-
ngOnInit() {
|
|
47
|
-
if (this.initialValue) {
|
|
48
|
-
this.formArray.patchValue(this.initialValue);
|
|
49
|
-
}
|
|
50
|
-
if (this.nodeGroupList.maxItems) {
|
|
51
|
-
this.maxItems = this.nodeGroupList.maxItems;
|
|
52
|
-
}
|
|
53
|
-
if (this.nodeGroupList.minItems) {
|
|
54
|
-
this.minItems = this.nodeGroupList.minItems;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
ngAfterViewInit() {
|
|
59
|
-
this.items.changes.subscribe(() => {
|
|
60
|
-
this.setLastEditable();
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
removeItem($index: number) {
|
|
65
|
-
if (this.formArray.length <= this.minItems) {
|
|
66
|
-
this.message.emit({
|
|
67
|
-
message: `You cannot remove the last ${this.nodeGroupList.type.name} configuration!`,
|
|
68
|
-
type: 'error',
|
|
69
|
-
})
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
this.formArray.removeAt($index);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
addItem() {
|
|
76
|
-
this.formArray.push(buildFormFromSchema(this.nodeGroupList.type, null));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
setLastEditable() {
|
|
80
|
-
const lastItem = this.items.last;
|
|
81
|
-
if (lastItem) {
|
|
82
|
-
lastItem.editable = this.editable;
|
|
83
|
-
}
|
|
84
|
-
this.cdr.detectChanges();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
asFormGroup(group: any) {
|
|
88
|
-
return group as FormGroup;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
getTitle($index: number) {
|
|
92
|
-
let title = `${this.nodeGroupList.type.label ?? this.nodeGroupList.type.name}`;
|
|
93
|
-
if (this.formArray.length > 1) {
|
|
94
|
-
return `${title} #${$index + 1}`;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
return title;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { FormArray, FormControl, FormGroup } from '@angular/forms';
|
|
2
|
-
|
|
3
|
-
export type LeafRuntimeType<T> = T extends 'string'
|
|
4
|
-
? string
|
|
5
|
-
: T extends 'number'
|
|
6
|
-
? number
|
|
7
|
-
: T extends 'boolean'
|
|
8
|
-
? boolean
|
|
9
|
-
: T extends 'enum'
|
|
10
|
-
? string | number
|
|
11
|
-
: never;
|
|
12
|
-
|
|
13
|
-
export type LeafBase = {
|
|
14
|
-
kind: 'leaf';
|
|
15
|
-
name: string;
|
|
16
|
-
required?: true | undefined;
|
|
17
|
-
label?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type AnonLeaf = {
|
|
22
|
-
[K in Leaf['type']]: { type: K };
|
|
23
|
-
}[Leaf['type']];
|
|
24
|
-
|
|
25
|
-
export type LeafString = LeafBase & {
|
|
26
|
-
type: 'string';
|
|
27
|
-
default?: LeafRuntimeType<'string'>;
|
|
28
|
-
};
|
|
29
|
-
export type LeafNumber = LeafBase & {
|
|
30
|
-
type: 'number';
|
|
31
|
-
default?: LeafRuntimeType<'number'>;
|
|
32
|
-
};
|
|
33
|
-
export type LeafBoolean = LeafBase & {
|
|
34
|
-
type: 'boolean';
|
|
35
|
-
default?: LeafRuntimeType<'boolean'>;
|
|
36
|
-
};
|
|
37
|
-
export type LeafEnum = LeafBase & {
|
|
38
|
-
type: 'enum';
|
|
39
|
-
default?: LeafRuntimeType<'enum'>;
|
|
40
|
-
enumLabel?: string[];
|
|
41
|
-
enum: LeafRuntimeType<'enum'>[];
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type Appearance = {
|
|
45
|
-
flatten?: boolean;
|
|
46
|
-
noBorder?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export type Leaf = LeafString | LeafNumber | LeafBoolean | LeafEnum;
|
|
50
|
-
|
|
51
|
-
export type LeafList<TKind extends Leaf['type'] = Leaf['type']> = {
|
|
52
|
-
kind: 'leafList';
|
|
53
|
-
label?: string;
|
|
54
|
-
name: string;
|
|
55
|
-
description?: string;
|
|
56
|
-
default?: Exclude<Leaf['default'], undefined>[];
|
|
57
|
-
type: TKind;
|
|
58
|
-
minItems?: number;
|
|
59
|
-
maxItems?: number;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type NodeGroupList = {
|
|
63
|
-
kind: 'nodeGroupList';
|
|
64
|
-
name: string;
|
|
65
|
-
label?: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
type: NodeGroup;
|
|
68
|
-
minItems?: number;
|
|
69
|
-
maxItems?: number;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type NodeGroup = {
|
|
73
|
-
kind: 'nodeGroup';
|
|
74
|
-
name: string;
|
|
75
|
-
subType?: string;
|
|
76
|
-
label?: string;
|
|
77
|
-
root?: boolean;
|
|
78
|
-
description?: string;
|
|
79
|
-
children: Record<string, NodeType>;
|
|
80
|
-
appearance?: Appearance;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export type NodeType = Leaf | LeafList | NodeGroup | NodeGroupList;
|
|
84
|
-
export type DFormControl<T extends NodeType> = T extends Leaf
|
|
85
|
-
? FormControl<LeafRuntimeType<T['type']>>
|
|
86
|
-
: T extends LeafList
|
|
87
|
-
? FormArray<FormControl<LeafRuntimeType<T['type']>>>
|
|
88
|
-
: T extends NodeGroup
|
|
89
|
-
? DFormGroup<T>
|
|
90
|
-
: T extends NodeGroupList
|
|
91
|
-
? FormArray<DFormGroup<T['type']>>
|
|
92
|
-
: never;
|
|
93
|
-
|
|
94
|
-
export type FormGroupType<T extends NodeGroup> = {
|
|
95
|
-
[TChild in keyof T['children']]: DFormControl<T['children'][TChild]>;
|
|
96
|
-
};
|
|
97
|
-
export type DFormGroup<T extends NodeGroup> = FormGroup<FormGroupType<T>>;
|