@ng-formworks/core 15.2.7
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/karma.conf.js +46 -0
- package/ng-package.json +11 -0
- package/package.json +54 -0
- package/src/lib/framework-library/framework-library.service.ts +195 -0
- package/src/lib/framework-library/framework.ts +11 -0
- package/src/lib/framework-library/no-framework.component.html +2 -0
- package/src/lib/framework-library/no-framework.component.ts +11 -0
- package/src/lib/framework-library/no-framework.module.ts +18 -0
- package/src/lib/framework-library/no.framework.ts +11 -0
- package/src/lib/json-schema-form.component.html +7 -0
- package/src/lib/json-schema-form.component.ts +809 -0
- package/src/lib/json-schema-form.module.ts +17 -0
- package/src/lib/json-schema-form.service.ts +907 -0
- package/src/lib/locale/de-validation-messages.ts +58 -0
- package/src/lib/locale/en-validation-messages.ts +58 -0
- package/src/lib/locale/es-validation-messages.ts +55 -0
- package/src/lib/locale/fr-validation-messages.ts +58 -0
- package/src/lib/locale/index.ts +7 -0
- package/src/lib/locale/it-validation-messages.ts +58 -0
- package/src/lib/locale/pt-validation-messages.ts +58 -0
- package/src/lib/locale/zh-validation-messages.ts +58 -0
- package/src/lib/locale-dates/en-US.ts +5 -0
- package/src/lib/shared/convert-schema-to-draft6.function.ts +321 -0
- package/src/lib/shared/form-group.functions.ts +522 -0
- package/src/lib/shared/format-regex.constants.ts +73 -0
- package/src/lib/shared/index.ts +40 -0
- package/src/lib/shared/json-schema.functions.ts +788 -0
- package/src/lib/shared/json.validators.ts +878 -0
- package/src/lib/shared/jsonpointer.functions.ts +1012 -0
- package/src/lib/shared/jspointer.functions.json.spec.ts +103 -0
- package/src/lib/shared/layout.functions.ts +1233 -0
- package/src/lib/shared/merge-schemas.function.ts +329 -0
- package/src/lib/shared/utility.functions.ts +373 -0
- package/src/lib/shared/validator.functions.spec.ts +55 -0
- package/src/lib/shared/validator.functions.ts +601 -0
- package/src/lib/widget-library/add-reference.component.ts +59 -0
- package/src/lib/widget-library/button.component.ts +54 -0
- package/src/lib/widget-library/checkbox.component.ts +74 -0
- package/src/lib/widget-library/checkboxes.component.ts +104 -0
- package/src/lib/widget-library/file.component.ts +36 -0
- package/src/lib/widget-library/hidden.component.ts +39 -0
- package/src/lib/widget-library/index.ts +56 -0
- package/src/lib/widget-library/input.component.ts +76 -0
- package/src/lib/widget-library/message.component.ts +29 -0
- package/src/lib/widget-library/none.component.ts +12 -0
- package/src/lib/widget-library/number.component.ts +79 -0
- package/src/lib/widget-library/one-of.component.ts +36 -0
- package/src/lib/widget-library/orderable.directive.ts +130 -0
- package/src/lib/widget-library/radios.component.ts +101 -0
- package/src/lib/widget-library/root.component.ts +78 -0
- package/src/lib/widget-library/section.component.ts +133 -0
- package/src/lib/widget-library/select-framework.component.ts +50 -0
- package/src/lib/widget-library/select-widget.component.ts +46 -0
- package/src/lib/widget-library/select.component.ts +96 -0
- package/src/lib/widget-library/submit.component.ts +68 -0
- package/src/lib/widget-library/tab.component.ts +29 -0
- package/src/lib/widget-library/tabs.component.ts +83 -0
- package/src/lib/widget-library/template.component.ts +52 -0
- package/src/lib/widget-library/textarea.component.ts +68 -0
- package/src/lib/widget-library/widget-library.module.ts +13 -0
- package/src/lib/widget-library/widget-library.service.ts +234 -0
- package/src/public_api.ts +21 -0
- package/src/test.ts +18 -0
- package/tsconfig.lib.json +25 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +17 -0
- package/tslint.json +11 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Directive,
|
|
3
|
+
ElementRef,
|
|
4
|
+
Input,
|
|
5
|
+
NgZone,
|
|
6
|
+
OnInit
|
|
7
|
+
} from '@angular/core';
|
|
8
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* OrderableDirective
|
|
13
|
+
*
|
|
14
|
+
* Enables array elements to be reordered by dragging and dropping.
|
|
15
|
+
*
|
|
16
|
+
* Only works for arrays that have at least two elements.
|
|
17
|
+
*
|
|
18
|
+
* Also detects arrays-within-arrays, and correctly moves either
|
|
19
|
+
* the child array element or the parent array element,
|
|
20
|
+
* depending on the drop targert.
|
|
21
|
+
*
|
|
22
|
+
* Listeners for movable element being dragged:
|
|
23
|
+
* - dragstart: add 'dragging' class to element, set effectAllowed = 'move'
|
|
24
|
+
* - dragover: set dropEffect = 'move'
|
|
25
|
+
* - dragend: remove 'dragging' class from element
|
|
26
|
+
*
|
|
27
|
+
* Listeners for stationary items being dragged over:
|
|
28
|
+
* - dragenter: add 'drag-target-...' classes to element
|
|
29
|
+
* - dragleave: remove 'drag-target-...' classes from element
|
|
30
|
+
* - drop: remove 'drag-target-...' classes from element, move dropped array item
|
|
31
|
+
*/
|
|
32
|
+
@Directive({
|
|
33
|
+
// tslint:disable-next-line:directive-selector
|
|
34
|
+
selector: '[orderable]',
|
|
35
|
+
})
|
|
36
|
+
export class OrderableDirective implements OnInit {
|
|
37
|
+
arrayLayoutIndex: string;
|
|
38
|
+
element: any;
|
|
39
|
+
overParentElement = false;
|
|
40
|
+
overChildElement = false;
|
|
41
|
+
@Input() orderable: boolean;
|
|
42
|
+
@Input() layoutNode: any;
|
|
43
|
+
@Input() layoutIndex: number[];
|
|
44
|
+
@Input() dataIndex: number[];
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
private elementRef: ElementRef,
|
|
48
|
+
private jsf: JsonSchemaFormService,
|
|
49
|
+
private ngZone: NgZone
|
|
50
|
+
) { }
|
|
51
|
+
|
|
52
|
+
ngOnInit() {
|
|
53
|
+
if (this.orderable && this.layoutNode && this.layoutIndex && this.dataIndex) {
|
|
54
|
+
this.element = this.elementRef.nativeElement;
|
|
55
|
+
this.element.draggable = true;
|
|
56
|
+
this.arrayLayoutIndex = 'move:' + this.layoutIndex.slice(0, -1).toString();
|
|
57
|
+
|
|
58
|
+
this.ngZone.runOutsideAngular(() => {
|
|
59
|
+
|
|
60
|
+
// Listeners for movable element being dragged:
|
|
61
|
+
|
|
62
|
+
this.element.addEventListener('dragstart', (event) => {
|
|
63
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
64
|
+
event.dataTransfer.setData('text', '');
|
|
65
|
+
// Hack to bypass stupid HTML drag-and-drop dataTransfer protection
|
|
66
|
+
// so drag source info will be available on dragenter
|
|
67
|
+
const sourceArrayIndex = this.dataIndex[this.dataIndex.length - 1];
|
|
68
|
+
sessionStorage.setItem(this.arrayLayoutIndex, sourceArrayIndex + '');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
this.element.addEventListener('dragover', (event) => {
|
|
72
|
+
if (event.preventDefault) { event.preventDefault(); }
|
|
73
|
+
event.dataTransfer.dropEffect = 'move';
|
|
74
|
+
return false;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Listeners for stationary items being dragged over:
|
|
78
|
+
|
|
79
|
+
this.element.addEventListener('dragenter', (event) => {
|
|
80
|
+
// Part 1 of a hack, inspired by Dragster, to simulate mouseover and mouseout
|
|
81
|
+
// behavior while dragging items - http://bensmithett.github.io/dragster/
|
|
82
|
+
if (this.overParentElement) {
|
|
83
|
+
return this.overChildElement = true;
|
|
84
|
+
} else {
|
|
85
|
+
this.overParentElement = true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const sourceArrayIndex = sessionStorage.getItem(this.arrayLayoutIndex);
|
|
89
|
+
if (sourceArrayIndex !== null) {
|
|
90
|
+
if (this.dataIndex[this.dataIndex.length - 1] < +sourceArrayIndex) {
|
|
91
|
+
this.element.classList.add('drag-target-top');
|
|
92
|
+
} else if (this.dataIndex[this.dataIndex.length - 1] > +sourceArrayIndex) {
|
|
93
|
+
this.element.classList.add('drag-target-bottom');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
this.element.addEventListener('dragleave', (event) => {
|
|
99
|
+
// Part 2 of the Dragster hack
|
|
100
|
+
if (this.overChildElement) {
|
|
101
|
+
this.overChildElement = false;
|
|
102
|
+
} else if (this.overParentElement) {
|
|
103
|
+
this.overParentElement = false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const sourceArrayIndex = sessionStorage.getItem(this.arrayLayoutIndex);
|
|
107
|
+
if (!this.overParentElement && !this.overChildElement && sourceArrayIndex !== null) {
|
|
108
|
+
this.element.classList.remove('drag-target-top');
|
|
109
|
+
this.element.classList.remove('drag-target-bottom');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
this.element.addEventListener('drop', (event) => {
|
|
114
|
+
this.element.classList.remove('drag-target-top');
|
|
115
|
+
this.element.classList.remove('drag-target-bottom');
|
|
116
|
+
// Confirm that drop target is another item in the same array as source item
|
|
117
|
+
const sourceArrayIndex = sessionStorage.getItem(this.arrayLayoutIndex);
|
|
118
|
+
const destArrayIndex = this.dataIndex[this.dataIndex.length - 1];
|
|
119
|
+
if (sourceArrayIndex !== null && +sourceArrayIndex !== destArrayIndex) {
|
|
120
|
+
// Move array item
|
|
121
|
+
this.jsf.moveArrayItem(this, +sourceArrayIndex, destArrayIndex);
|
|
122
|
+
}
|
|
123
|
+
sessionStorage.removeItem(this.arrayLayoutIndex);
|
|
124
|
+
return false;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { AbstractControl } from '@angular/forms';
|
|
2
|
+
import { buildTitleMap } from '../shared';
|
|
3
|
+
import { Component, Input, OnInit } from '@angular/core';
|
|
4
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
// tslint:disable-next-line:component-selector
|
|
9
|
+
selector: 'radios-widget',
|
|
10
|
+
template: `
|
|
11
|
+
<label *ngIf="options?.title"
|
|
12
|
+
[attr.for]="'control' + layoutNode?._id"
|
|
13
|
+
[class]="options?.labelHtmlClass || ''"
|
|
14
|
+
[style.display]="options?.notitle ? 'none' : ''"
|
|
15
|
+
[innerHTML]="options?.title"></label>
|
|
16
|
+
|
|
17
|
+
<!-- 'horizontal' = radios-inline or radiobuttons -->
|
|
18
|
+
<div *ngIf="layoutOrientation === 'horizontal'"
|
|
19
|
+
[class]="options?.htmlClass || ''">
|
|
20
|
+
<label *ngFor="let radioItem of radiosList"
|
|
21
|
+
[attr.for]="'control' + layoutNode?._id + '/' + radioItem?.value"
|
|
22
|
+
[class]="(options?.itemLabelHtmlClass || '') +
|
|
23
|
+
((controlValue + '' === radioItem?.value + '') ?
|
|
24
|
+
(' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
|
|
25
|
+
(' ' + (options?.style?.unselected || '')))">
|
|
26
|
+
<input type="radio"
|
|
27
|
+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
|
|
28
|
+
[attr.readonly]="options?.readonly ? 'readonly' : null"
|
|
29
|
+
[attr.required]="options?.required"
|
|
30
|
+
[checked]="radioItem?.value === controlValue"
|
|
31
|
+
[class]="options?.fieldHtmlClass || ''"
|
|
32
|
+
[disabled]="controlDisabled"
|
|
33
|
+
[id]="'control' + layoutNode?._id + '/' + radioItem?.value"
|
|
34
|
+
[name]="controlName"
|
|
35
|
+
[value]="radioItem?.value"
|
|
36
|
+
(change)="updateValue($event)">
|
|
37
|
+
<span [innerHTML]="radioItem?.name"></span>
|
|
38
|
+
</label>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- 'vertical' = regular radios -->
|
|
42
|
+
<div *ngIf="layoutOrientation !== 'horizontal'">
|
|
43
|
+
<div *ngFor="let radioItem of radiosList"
|
|
44
|
+
[class]="options?.htmlClass || ''">
|
|
45
|
+
<label
|
|
46
|
+
[attr.for]="'control' + layoutNode?._id + '/' + radioItem?.value"
|
|
47
|
+
[class]="(options?.itemLabelHtmlClass || '') +
|
|
48
|
+
((controlValue + '' === radioItem?.value + '') ?
|
|
49
|
+
(' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
|
|
50
|
+
(' ' + (options?.style?.unselected || '')))">
|
|
51
|
+
<input type="radio"
|
|
52
|
+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
|
|
53
|
+
[attr.readonly]="options?.readonly ? 'readonly' : null"
|
|
54
|
+
[attr.required]="options?.required"
|
|
55
|
+
[checked]="radioItem?.value === controlValue"
|
|
56
|
+
[class]="options?.fieldHtmlClass || ''"
|
|
57
|
+
[disabled]="controlDisabled"
|
|
58
|
+
[id]="'control' + layoutNode?._id + '/' + radioItem?.value"
|
|
59
|
+
[name]="controlName"
|
|
60
|
+
[value]="radioItem?.value"
|
|
61
|
+
(change)="updateValue($event)">
|
|
62
|
+
<span [innerHTML]="radioItem?.name"></span>
|
|
63
|
+
</label>
|
|
64
|
+
</div>
|
|
65
|
+
</div>`,
|
|
66
|
+
})
|
|
67
|
+
export class RadiosComponent implements OnInit {
|
|
68
|
+
formControl: AbstractControl;
|
|
69
|
+
controlName: string;
|
|
70
|
+
controlValue: any;
|
|
71
|
+
controlDisabled = false;
|
|
72
|
+
boundControl = false;
|
|
73
|
+
options: any;
|
|
74
|
+
layoutOrientation = 'vertical';
|
|
75
|
+
radiosList: any[] = [];
|
|
76
|
+
@Input() layoutNode: any;
|
|
77
|
+
@Input() layoutIndex: number[];
|
|
78
|
+
@Input() dataIndex: number[];
|
|
79
|
+
|
|
80
|
+
constructor(
|
|
81
|
+
private jsf: JsonSchemaFormService
|
|
82
|
+
) { }
|
|
83
|
+
|
|
84
|
+
ngOnInit() {
|
|
85
|
+
this.options = this.layoutNode.options || {};
|
|
86
|
+
if (this.layoutNode.type === 'radios-inline' ||
|
|
87
|
+
this.layoutNode.type === 'radiobuttons'
|
|
88
|
+
) {
|
|
89
|
+
this.layoutOrientation = 'horizontal';
|
|
90
|
+
}
|
|
91
|
+
this.radiosList = buildTitleMap(
|
|
92
|
+
this.options.titleMap || this.options.enumNames,
|
|
93
|
+
this.options.enum, true
|
|
94
|
+
);
|
|
95
|
+
this.jsf.initializeControl(this);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
updateValue(event) {
|
|
99
|
+
this.jsf.updateValue(this, event.target.value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Component, Input } from '@angular/core';
|
|
2
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
// tslint:disable-next-line:component-selector
|
|
7
|
+
selector: 'root-widget',
|
|
8
|
+
template: `
|
|
9
|
+
<div *ngFor="let layoutItem of layout; let i = index"
|
|
10
|
+
[class.form-flex-item]="isFlexItem"
|
|
11
|
+
[style.align-self]="(layoutItem.options || {})['align-self']"
|
|
12
|
+
[style.flex-basis]="getFlexAttribute(layoutItem, 'flex-basis')"
|
|
13
|
+
[style.flex-grow]="getFlexAttribute(layoutItem, 'flex-grow')"
|
|
14
|
+
[style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
|
|
15
|
+
[style.order]="(layoutItem.options || {}).order">
|
|
16
|
+
<div
|
|
17
|
+
[dataIndex]="layoutItem?.arrayItem ? (dataIndex || []).concat(i) : (dataIndex || [])"
|
|
18
|
+
[layoutIndex]="(layoutIndex || []).concat(i)"
|
|
19
|
+
[layoutNode]="layoutItem"
|
|
20
|
+
[orderable]="isDraggable(layoutItem)">
|
|
21
|
+
<select-framework-widget *ngIf="showWidget(layoutItem)"
|
|
22
|
+
[dataIndex]="layoutItem?.arrayItem ? (dataIndex || []).concat(i) : (dataIndex || [])"
|
|
23
|
+
[layoutIndex]="(layoutIndex || []).concat(i)"
|
|
24
|
+
[layoutNode]="layoutItem"></select-framework-widget>
|
|
25
|
+
</div>
|
|
26
|
+
</div>`,
|
|
27
|
+
styles: [`
|
|
28
|
+
[draggable=true] {
|
|
29
|
+
transition: all 150ms cubic-bezier(.4, 0, .2, 1);
|
|
30
|
+
}
|
|
31
|
+
[draggable=true]:hover {
|
|
32
|
+
cursor: move;
|
|
33
|
+
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
34
|
+
position: relative; z-index: 10;
|
|
35
|
+
margin-top: -1px;
|
|
36
|
+
margin-left: -1px;
|
|
37
|
+
margin-right: 1px;
|
|
38
|
+
margin-bottom: 1px;
|
|
39
|
+
}
|
|
40
|
+
[draggable=true].drag-target-top {
|
|
41
|
+
box-shadow: 0 -2px 0 #000;
|
|
42
|
+
position: relative; z-index: 20;
|
|
43
|
+
}
|
|
44
|
+
[draggable=true].drag-target-bottom {
|
|
45
|
+
box-shadow: 0 2px 0 #000;
|
|
46
|
+
position: relative; z-index: 20;
|
|
47
|
+
}
|
|
48
|
+
`],
|
|
49
|
+
})
|
|
50
|
+
export class RootComponent {
|
|
51
|
+
options: any;
|
|
52
|
+
@Input() dataIndex: number[];
|
|
53
|
+
@Input() layoutIndex: number[];
|
|
54
|
+
@Input() layout: any[];
|
|
55
|
+
@Input() isOrderable: boolean;
|
|
56
|
+
@Input() isFlexItem = false;
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
private jsf: JsonSchemaFormService
|
|
60
|
+
) { }
|
|
61
|
+
|
|
62
|
+
isDraggable(node: any): boolean {
|
|
63
|
+
return node.arrayItem && node.type !== '$ref' &&
|
|
64
|
+
node.arrayItemType === 'list' && this.isOrderable !== false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Set attributes for flexbox child
|
|
68
|
+
// (container attributes are set in section.component)
|
|
69
|
+
getFlexAttribute(node: any, attribute: string) {
|
|
70
|
+
const index = ['flex-grow', 'flex-shrink', 'flex-basis'].indexOf(attribute);
|
|
71
|
+
return ((node.options || {}).flex || '').split(/\s+/)[index] ||
|
|
72
|
+
(node.options || {})[attribute] || ['1', '1', 'auto'][index];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
showWidget(layoutNode: any): boolean {
|
|
76
|
+
return this.jsf.evaluateCondition(layoutNode, this.dataIndex);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Component, Input, OnInit } from '@angular/core';
|
|
2
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
// tslint:disable-next-line:component-selector
|
|
7
|
+
selector: 'section-widget',
|
|
8
|
+
template: `
|
|
9
|
+
<div *ngIf="containerType === 'div'"
|
|
10
|
+
[class]="options?.htmlClass || ''"
|
|
11
|
+
[class.expandable]="options?.expandable && !expanded"
|
|
12
|
+
[class.expanded]="options?.expandable && expanded">
|
|
13
|
+
<label *ngIf="sectionTitle"
|
|
14
|
+
class="legend"
|
|
15
|
+
[class]="options?.labelHtmlClass || ''"
|
|
16
|
+
[innerHTML]="sectionTitle"
|
|
17
|
+
(click)="toggleExpanded()"></label>
|
|
18
|
+
<root-widget *ngIf="expanded"
|
|
19
|
+
[dataIndex]="dataIndex"
|
|
20
|
+
[layout]="layoutNode.items"
|
|
21
|
+
[layoutIndex]="layoutIndex"
|
|
22
|
+
[isFlexItem]="getFlexAttribute('is-flex')"
|
|
23
|
+
[isOrderable]="options?.orderable"
|
|
24
|
+
[class.form-flex-column]="getFlexAttribute('flex-direction') === 'column'"
|
|
25
|
+
[class.form-flex-row]="getFlexAttribute('flex-direction') === 'row'"
|
|
26
|
+
[style.align-content]="getFlexAttribute('align-content')"
|
|
27
|
+
[style.align-items]="getFlexAttribute('align-items')"
|
|
28
|
+
[style.display]="getFlexAttribute('display')"
|
|
29
|
+
[style.flex-direction]="getFlexAttribute('flex-direction')"
|
|
30
|
+
[style.flex-wrap]="getFlexAttribute('flex-wrap')"
|
|
31
|
+
[style.justify-content]="getFlexAttribute('justify-content')"></root-widget>
|
|
32
|
+
</div>
|
|
33
|
+
<fieldset *ngIf="containerType === 'fieldset'"
|
|
34
|
+
[class]="options?.htmlClass || ''"
|
|
35
|
+
[class.expandable]="options?.expandable && !expanded"
|
|
36
|
+
[class.expanded]="options?.expandable && expanded"
|
|
37
|
+
[disabled]="options?.readonly">
|
|
38
|
+
<legend *ngIf="sectionTitle"
|
|
39
|
+
class="legend"
|
|
40
|
+
[class]="options?.labelHtmlClass || ''"
|
|
41
|
+
[innerHTML]="sectionTitle"
|
|
42
|
+
(click)="toggleExpanded()"></legend>
|
|
43
|
+
<div *ngIf="options?.messageLocation !== 'bottom'">
|
|
44
|
+
<p *ngIf="options?.description"
|
|
45
|
+
class="help-block"
|
|
46
|
+
[class]="options?.labelHelpBlockClass || ''"
|
|
47
|
+
[innerHTML]="options?.description"></p>
|
|
48
|
+
</div>
|
|
49
|
+
<root-widget *ngIf="expanded"
|
|
50
|
+
[dataIndex]="dataIndex"
|
|
51
|
+
[layout]="layoutNode.items"
|
|
52
|
+
[layoutIndex]="layoutIndex"
|
|
53
|
+
[isFlexItem]="getFlexAttribute('is-flex')"
|
|
54
|
+
[isOrderable]="options?.orderable"
|
|
55
|
+
[class.form-flex-column]="getFlexAttribute('flex-direction') === 'column'"
|
|
56
|
+
[class.form-flex-row]="getFlexAttribute('flex-direction') === 'row'"
|
|
57
|
+
[style.align-content]="getFlexAttribute('align-content')"
|
|
58
|
+
[style.align-items]="getFlexAttribute('align-items')"
|
|
59
|
+
[style.display]="getFlexAttribute('display')"
|
|
60
|
+
[style.flex-direction]="getFlexAttribute('flex-direction')"
|
|
61
|
+
[style.flex-wrap]="getFlexAttribute('flex-wrap')"
|
|
62
|
+
[style.justify-content]="getFlexAttribute('justify-content')"></root-widget>
|
|
63
|
+
<div *ngIf="options?.messageLocation === 'bottom'">
|
|
64
|
+
<p *ngIf="options?.description"
|
|
65
|
+
class="help-block"
|
|
66
|
+
[class]="options?.labelHelpBlockClass || ''"
|
|
67
|
+
[innerHTML]="options?.description"></p>
|
|
68
|
+
</div>
|
|
69
|
+
</fieldset>`,
|
|
70
|
+
styles: [`
|
|
71
|
+
.legend { font-weight: bold; }
|
|
72
|
+
.expandable > legend:before, .expandable > label:before { content: '▶'; padding-right: .3em; }
|
|
73
|
+
.expanded > legend:before, .expanded > label:before { content: '▼'; padding-right: .2em; }
|
|
74
|
+
`],
|
|
75
|
+
})
|
|
76
|
+
export class SectionComponent implements OnInit {
|
|
77
|
+
options: any;
|
|
78
|
+
expanded = true;
|
|
79
|
+
containerType: string;
|
|
80
|
+
@Input() layoutNode: any;
|
|
81
|
+
@Input() layoutIndex: number[];
|
|
82
|
+
@Input() dataIndex: number[];
|
|
83
|
+
|
|
84
|
+
constructor(
|
|
85
|
+
private jsf: JsonSchemaFormService
|
|
86
|
+
) { }
|
|
87
|
+
|
|
88
|
+
get sectionTitle() {
|
|
89
|
+
return this.options.notitle ? null : this.jsf.setItemTitle(this);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ngOnInit() {
|
|
93
|
+
this.jsf.initializeControl(this);
|
|
94
|
+
this.options = this.layoutNode.options || {};
|
|
95
|
+
this.expanded = typeof this.options.expanded === 'boolean' ?
|
|
96
|
+
this.options.expanded : !this.options.expandable;
|
|
97
|
+
switch (this.layoutNode.type) {
|
|
98
|
+
case 'fieldset': case 'array': case 'tab': case 'advancedfieldset':
|
|
99
|
+
case 'authfieldset': case 'optionfieldset': case 'selectfieldset':
|
|
100
|
+
this.containerType = 'fieldset';
|
|
101
|
+
break;
|
|
102
|
+
default: // 'div', 'flex', 'section', 'conditional', 'actions', 'tagsinput'
|
|
103
|
+
this.containerType = 'div';
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
toggleExpanded() {
|
|
109
|
+
if (this.options.expandable) { this.expanded = !this.expanded; }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Set attributes for flexbox container
|
|
113
|
+
// (child attributes are set in root.component)
|
|
114
|
+
getFlexAttribute(attribute: string) {
|
|
115
|
+
const flexActive: boolean =
|
|
116
|
+
this.layoutNode.type === 'flex' ||
|
|
117
|
+
!!this.options.displayFlex ||
|
|
118
|
+
this.options.display === 'flex';
|
|
119
|
+
if (attribute !== 'flex' && !flexActive) { return null; }
|
|
120
|
+
switch (attribute) {
|
|
121
|
+
case 'is-flex':
|
|
122
|
+
return flexActive;
|
|
123
|
+
case 'display':
|
|
124
|
+
return flexActive ? 'flex' : 'initial';
|
|
125
|
+
case 'flex-direction': case 'flex-wrap':
|
|
126
|
+
const index = ['flex-direction', 'flex-wrap'].indexOf(attribute);
|
|
127
|
+
return (this.options['flex-flow'] || '').split(/\s+/)[index] ||
|
|
128
|
+
this.options[attribute] || ['column', 'nowrap'][index];
|
|
129
|
+
case 'justify-content': case 'align-items': case 'align-content':
|
|
130
|
+
return this.options[attribute];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component, ComponentFactoryResolver, ComponentRef, Input,
|
|
3
|
+
OnChanges, OnInit, ViewChild, ViewContainerRef
|
|
4
|
+
} from '@angular/core';
|
|
5
|
+
|
|
6
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
// tslint:disable-next-line:component-selector
|
|
10
|
+
selector: 'select-framework-widget',
|
|
11
|
+
template: `<div #widgetContainer></div>`,
|
|
12
|
+
})
|
|
13
|
+
export class SelectFrameworkComponent implements OnChanges, OnInit {
|
|
14
|
+
newComponent: ComponentRef<any> = null;
|
|
15
|
+
@Input() layoutNode: any;
|
|
16
|
+
@Input() layoutIndex: number[];
|
|
17
|
+
@Input() dataIndex: number[];
|
|
18
|
+
@ViewChild('widgetContainer', {
|
|
19
|
+
read: ViewContainerRef,
|
|
20
|
+
static: true })
|
|
21
|
+
widgetContainer: ViewContainerRef;
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
private componentFactory: ComponentFactoryResolver,
|
|
25
|
+
private jsf: JsonSchemaFormService
|
|
26
|
+
) { }
|
|
27
|
+
|
|
28
|
+
ngOnInit() {
|
|
29
|
+
this.updateComponent();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ngOnChanges() {
|
|
33
|
+
this.updateComponent();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
updateComponent() {
|
|
37
|
+
if (this.widgetContainer && !this.newComponent && this.jsf.framework) {
|
|
38
|
+
this.newComponent = this.widgetContainer.createComponent(
|
|
39
|
+
this.componentFactory.resolveComponentFactory(this.jsf.framework)
|
|
40
|
+
);
|
|
41
|
+
//TODO fix all deprecated calls and test
|
|
42
|
+
//this.widgetContainer.createComponent<any>(this.jsf.framework)
|
|
43
|
+
}
|
|
44
|
+
if (this.newComponent) {
|
|
45
|
+
for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
|
|
46
|
+
this.newComponent.instance[input] = this[input];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component, ComponentFactoryResolver, ComponentRef, Input,
|
|
3
|
+
OnChanges, OnInit, ViewChild, ViewContainerRef
|
|
4
|
+
} from '@angular/core';
|
|
5
|
+
|
|
6
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
// tslint:disable-next-line:component-selector
|
|
10
|
+
selector: 'select-widget-widget',
|
|
11
|
+
template: `<div #widgetContainer></div>`,
|
|
12
|
+
})
|
|
13
|
+
export class SelectWidgetComponent implements OnChanges, OnInit {
|
|
14
|
+
newComponent: ComponentRef<any> = null;
|
|
15
|
+
@Input() layoutNode: any;
|
|
16
|
+
@Input() layoutIndex: number[];
|
|
17
|
+
@Input() dataIndex: number[];
|
|
18
|
+
@ViewChild('widgetContainer', { read: ViewContainerRef, static: true })
|
|
19
|
+
widgetContainer: ViewContainerRef;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
private componentFactory: ComponentFactoryResolver,
|
|
23
|
+
private jsf: JsonSchemaFormService
|
|
24
|
+
) { }
|
|
25
|
+
|
|
26
|
+
ngOnInit() {
|
|
27
|
+
this.updateComponent();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
ngOnChanges() {
|
|
31
|
+
this.updateComponent();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
updateComponent() {
|
|
35
|
+
if (this.widgetContainer && !this.newComponent && (this.layoutNode || {}).widget) {
|
|
36
|
+
this.newComponent = this.widgetContainer.createComponent(
|
|
37
|
+
this.componentFactory.resolveComponentFactory(this.layoutNode.widget)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
if (this.newComponent) {
|
|
41
|
+
for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
|
|
42
|
+
this.newComponent.instance[input] = this[input];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AbstractControl } from '@angular/forms';
|
|
2
|
+
import { buildTitleMap, isArray } from '../shared';
|
|
3
|
+
import { Component, Input, OnInit } from '@angular/core';
|
|
4
|
+
import { JsonSchemaFormService } from '../json-schema-form.service';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
// tslint:disable-next-line:component-selector
|
|
9
|
+
selector: 'select-widget',
|
|
10
|
+
template: `
|
|
11
|
+
<div
|
|
12
|
+
[class]="options?.htmlClass || ''">
|
|
13
|
+
<label *ngIf="options?.title"
|
|
14
|
+
[attr.for]="'control' + layoutNode?._id"
|
|
15
|
+
[class]="options?.labelHtmlClass || ''"
|
|
16
|
+
[style.display]="options?.notitle ? 'none' : ''"
|
|
17
|
+
[innerHTML]="options?.title"></label>
|
|
18
|
+
<select *ngIf="boundControl"
|
|
19
|
+
[formControl]="formControl"
|
|
20
|
+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
|
|
21
|
+
[attr.readonly]="options?.readonly ? 'readonly' : null"
|
|
22
|
+
[attr.required]="options?.required"
|
|
23
|
+
[class]="options?.fieldHtmlClass || ''"
|
|
24
|
+
[id]="'control' + layoutNode?._id"
|
|
25
|
+
[name]="controlName">
|
|
26
|
+
<ng-template ngFor let-selectItem [ngForOf]="selectList">
|
|
27
|
+
<option *ngIf="!isArray(selectItem?.items)"
|
|
28
|
+
[value]="selectItem?.value">
|
|
29
|
+
<span [innerHTML]="selectItem?.name"></span>
|
|
30
|
+
</option>
|
|
31
|
+
<optgroup *ngIf="isArray(selectItem?.items)"
|
|
32
|
+
[label]="selectItem?.group">
|
|
33
|
+
<option *ngFor="let subItem of selectItem.items"
|
|
34
|
+
[value]="subItem?.value">
|
|
35
|
+
<span [innerHTML]="subItem?.name"></span>
|
|
36
|
+
</option>
|
|
37
|
+
</optgroup>
|
|
38
|
+
</ng-template>
|
|
39
|
+
</select>
|
|
40
|
+
<select *ngIf="!boundControl"
|
|
41
|
+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
|
|
42
|
+
[attr.readonly]="options?.readonly ? 'readonly' : null"
|
|
43
|
+
[attr.required]="options?.required"
|
|
44
|
+
[class]="options?.fieldHtmlClass || ''"
|
|
45
|
+
[disabled]="controlDisabled"
|
|
46
|
+
[id]="'control' + layoutNode?._id"
|
|
47
|
+
[name]="controlName"
|
|
48
|
+
(change)="updateValue($event)">
|
|
49
|
+
<ng-template ngFor let-selectItem [ngForOf]="selectList">
|
|
50
|
+
<option *ngIf="!isArray(selectItem?.items)"
|
|
51
|
+
[selected]="selectItem?.value === controlValue"
|
|
52
|
+
[value]="selectItem?.value">
|
|
53
|
+
<span [innerHTML]="selectItem?.name"></span>
|
|
54
|
+
</option>
|
|
55
|
+
<optgroup *ngIf="isArray(selectItem?.items)"
|
|
56
|
+
[label]="selectItem?.group">
|
|
57
|
+
<option *ngFor="let subItem of selectItem.items"
|
|
58
|
+
[attr.selected]="subItem?.value === controlValue"
|
|
59
|
+
[value]="subItem?.value">
|
|
60
|
+
<span [innerHTML]="subItem?.name"></span>
|
|
61
|
+
</option>
|
|
62
|
+
</optgroup>
|
|
63
|
+
</ng-template>
|
|
64
|
+
</select>
|
|
65
|
+
</div>`,
|
|
66
|
+
})
|
|
67
|
+
export class SelectComponent implements OnInit {
|
|
68
|
+
formControl: AbstractControl;
|
|
69
|
+
controlName: string;
|
|
70
|
+
controlValue: any;
|
|
71
|
+
controlDisabled = false;
|
|
72
|
+
boundControl = false;
|
|
73
|
+
options: any;
|
|
74
|
+
selectList: any[] = [];
|
|
75
|
+
isArray = isArray;
|
|
76
|
+
@Input() layoutNode: any;
|
|
77
|
+
@Input() layoutIndex: number[];
|
|
78
|
+
@Input() dataIndex: number[];
|
|
79
|
+
|
|
80
|
+
constructor(
|
|
81
|
+
private jsf: JsonSchemaFormService
|
|
82
|
+
) { }
|
|
83
|
+
|
|
84
|
+
ngOnInit() {
|
|
85
|
+
this.options = this.layoutNode.options || {};
|
|
86
|
+
this.selectList = buildTitleMap(
|
|
87
|
+
this.options.titleMap || this.options.enumNames,
|
|
88
|
+
this.options.enum, !!this.options.required, !!this.options.flatList
|
|
89
|
+
);
|
|
90
|
+
this.jsf.initializeControl(this);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
updateValue(event) {
|
|
94
|
+
this.jsf.updateValue(this, event.target.value);
|
|
95
|
+
}
|
|
96
|
+
}
|