UI: Import/Export support for dynamic form properties. Improve json schema form to dynamic form conversion.
This commit is contained in:
parent
9fe5f7199e
commit
5840bf1f67
@ -21,7 +21,36 @@
|
|||||||
<div class="tb-form-table-header-cell tb-id-header" translate>dynamic-form.property.id</div>
|
<div class="tb-form-table-header-cell tb-id-header" translate>dynamic-form.property.id</div>
|
||||||
<div class="tb-form-table-header-cell tb-name-header" translate>dynamic-form.property.name</div>
|
<div class="tb-form-table-header-cell tb-name-header" translate>dynamic-form.property.name</div>
|
||||||
<div class="tb-form-table-header-cell tb-type-header" translate>dynamic-form.property.type</div>
|
<div class="tb-form-table-header-cell tb-type-header" translate>dynamic-form.property.type</div>
|
||||||
<div class="tb-form-table-header-cell tb-actions-header" [class.disabled]="disabled"></div>
|
<div class="tb-form-table-header-cell tb-actions-header tb-form-table-row-cell-buttons justify-end" [class.disabled]="disabled">
|
||||||
|
<button mat-icon-button
|
||||||
|
*ngIf="!disabled && importExport"
|
||||||
|
type="button"
|
||||||
|
matTooltip="{{ 'dynamic-form.import-form' | translate }}"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
(click)="import($event)">
|
||||||
|
<tb-icon>mdi:file-import</tb-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button
|
||||||
|
*ngIf="!disabled && importExport"
|
||||||
|
[class.tb-hidden]="!propertiesFormArray().controls.length"
|
||||||
|
type="button"
|
||||||
|
[disabled]="!propertiesFormArray().controls.length"
|
||||||
|
matTooltip="{{ 'dynamic-form.export-form' | translate }}"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
(click)="export($event)">
|
||||||
|
<tb-icon>mdi:file-export</tb-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button
|
||||||
|
*ngIf="!disabled"
|
||||||
|
[class.tb-hidden]="!propertiesFormArray().controls.length"
|
||||||
|
type="button"
|
||||||
|
[disabled]="!propertiesFormArray().controls.length"
|
||||||
|
matTooltip="{{ 'dynamic-form.clear-form' | translate }}"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
(click)="clear($event)">
|
||||||
|
<tb-icon>mdi:broom</tb-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="propertiesFormArray().controls.length; else noProperties" class="tb-form-table-body tb-drop-list"
|
<div *ngIf="propertiesFormArray().controls.length; else noProperties" class="tb-form-table-body tb-drop-list"
|
||||||
cdkDropList cdkDropListOrientation="vertical"
|
cdkDropList cdkDropListOrientation="vertical"
|
||||||
|
|||||||
@ -43,6 +43,8 @@ import {
|
|||||||
} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component';
|
} from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-row.component';
|
||||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { ImportExportService } from '@shared/import-export/import-export.service';
|
||||||
|
import { DialogService } from '@core/services/dialog.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-dynamic-form-properties',
|
selector: 'tb-dynamic-form-properties',
|
||||||
@ -89,6 +91,13 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI
|
|||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
fillHeight = false;
|
fillHeight = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
importExport = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
exportFileName = 'form';
|
||||||
|
|
||||||
booleanPropertyIds: string[] = [];
|
booleanPropertyIds: string[] = [];
|
||||||
|
|
||||||
propertiesFormGroup: UntypedFormGroup;
|
propertiesFormGroup: UntypedFormGroup;
|
||||||
@ -103,7 +112,9 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI
|
|||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder,
|
constructor(private fb: UntypedFormBuilder,
|
||||||
private destroyRef: DestroyRef,
|
private destroyRef: DestroyRef,
|
||||||
private translate: TranslateService) {
|
private translate: TranslateService,
|
||||||
|
private importExportService: ImportExportService,
|
||||||
|
private dialogService: DialogService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -114,10 +125,7 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI
|
|||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(
|
).subscribe(
|
||||||
() => {
|
() => {
|
||||||
let properties: FormProperty[] = this.propertiesFormGroup.get('properties').value;
|
const properties = this.getProperties();
|
||||||
if (properties) {
|
|
||||||
properties = properties.filter(p => propertyValid(p));
|
|
||||||
}
|
|
||||||
this.booleanPropertyIds = properties.filter(p => p.type === FormPropertyType.switch).map(p => p.id);
|
this.booleanPropertyIds = properties.filter(p => p.type === FormPropertyType.switch).map(p => p.id);
|
||||||
properties.forEach((p, i) => {
|
properties.forEach((p, i) => {
|
||||||
if (p.disableOnProperty && !this.booleanPropertyIds.includes(p.disableOnProperty)) {
|
if (p.disableOnProperty && !this.booleanPropertyIds.includes(p.disableOnProperty)) {
|
||||||
@ -222,6 +230,38 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export($event: Event) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
const properties = this.getProperties();
|
||||||
|
this.importExportService.exportFormProperties(properties, this.exportFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
import($event: Event) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
this.importExportService.importFormProperties().subscribe((properties) => {
|
||||||
|
if (properties) {
|
||||||
|
this.propertiesFormGroup.setControl('properties', this.preparePropertiesFormArray(properties), {emitEvent: true});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clear($event: Event) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
this.dialogService.confirm(this.translate.instant('dynamic-form.clear-form'),
|
||||||
|
this.translate.instant('dynamic-form.clear-form-prompt'), null, this.translate.instant('action.clear'))
|
||||||
|
.subscribe((clear) => {
|
||||||
|
if (clear) {
|
||||||
|
(this.propertiesFormGroup.get('properties') as UntypedFormArray).clear({emitEvent: true});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private preparePropertiesFormArray(properties: FormProperty[] | undefined): UntypedFormArray {
|
private preparePropertiesFormArray(properties: FormProperty[] | undefined): UntypedFormArray {
|
||||||
const propertiesControls: Array<AbstractControl> = [];
|
const propertiesControls: Array<AbstractControl> = [];
|
||||||
if (properties) {
|
if (properties) {
|
||||||
@ -231,4 +271,12 @@ export class DynamicFormPropertiesComponent implements ControlValueAccessor, OnI
|
|||||||
}
|
}
|
||||||
return this.fb.array(propertiesControls);
|
return this.fb.array(propertiesControls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getProperties(): FormProperty[] {
|
||||||
|
let properties: FormProperty[] = this.propertiesFormGroup.get('properties').value;
|
||||||
|
if (properties) {
|
||||||
|
properties = properties.filter(p => propertyValid(p));
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -267,6 +267,22 @@
|
|||||||
{{ 'dynamic-form.property.allow-empty-select-option' | translate }}
|
{{ 'dynamic-form.property.allow-empty-select-option' | translate }}
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="propertyItemType === FormPropertyType.select && propertyFormGroup.get('multiple').value" class="tb-form-row space-between">
|
||||||
|
<div translate>dynamic-form.property.selected-options-limit</div>
|
||||||
|
<div class="flex flex-1 flex-row items-center justify-end gap-2">
|
||||||
|
<div class="tb-small-label" translate>dynamic-form.property.min</div>
|
||||||
|
<mat-form-field appearance="outline" class="number flex" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="minItems"
|
||||||
|
type="number" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-divider vertical></mat-divider>
|
||||||
|
<div class="tb-small-label" translate>dynamic-form.property.max</div>
|
||||||
|
<mat-form-field appearance="outline" class="number flex" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="maxItems"
|
||||||
|
type="number" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<tb-dynamic-form-select-items
|
<tb-dynamic-form-select-items
|
||||||
formControlName="items">
|
formControlName="items">
|
||||||
</tb-dynamic-form-select-items>
|
</tb-dynamic-form-select-items>
|
||||||
|
|||||||
@ -124,6 +124,8 @@ export class DynamicFormPropertyPanelComponent implements OnInit {
|
|||||||
properties: [this.property.properties, []],
|
properties: [this.property.properties, []],
|
||||||
multiple: [this.property.multiple, []],
|
multiple: [this.property.multiple, []],
|
||||||
allowEmptyOption: [this.property.allowEmptyOption, []],
|
allowEmptyOption: [this.property.allowEmptyOption, []],
|
||||||
|
minItems: [this.property.minItems, []],
|
||||||
|
maxItems: [this.property.maxItems, []],
|
||||||
items: [this.property.items, []],
|
items: [this.property.items, []],
|
||||||
helpId: [this.property.helpId, []],
|
helpId: [this.property.helpId, []],
|
||||||
direction: [this.property.direction || 'column', []],
|
direction: [this.property.direction || 'column', []],
|
||||||
@ -208,13 +210,19 @@ export class DynamicFormPropertyPanelComponent implements OnInit {
|
|||||||
const multiple: boolean = this.propertyFormGroup.get('multiple').value;
|
const multiple: boolean = this.propertyFormGroup.get('multiple').value;
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false});
|
this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('minItems').enable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('maxItems').enable({emitEvent: false});
|
||||||
} else {
|
} else {
|
||||||
this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false});
|
this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('minItems').disable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('maxItems').disable({emitEvent: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.propertyFormGroup.get('multiple').disable({emitEvent: false});
|
this.propertyFormGroup.get('multiple').disable({emitEvent: false});
|
||||||
this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false});
|
this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('minItems').disable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('maxItems').disable({emitEvent: false});
|
||||||
this.propertyFormGroup.get('items').disable({emitEvent: false});
|
this.propertyFormGroup.get('items').disable({emitEvent: false});
|
||||||
}
|
}
|
||||||
if (type === FormPropertyType.datetime) {
|
if (type === FormPropertyType.datetime) {
|
||||||
@ -290,7 +298,9 @@ export class DynamicFormPropertyPanelComponent implements OnInit {
|
|||||||
this.propertyFormGroup.get('default').patchValue(newVal, {emitEvent: false});
|
this.propertyFormGroup.get('default').patchValue(newVal, {emitEvent: false});
|
||||||
}
|
}
|
||||||
this.propertyFormGroup.get('allowEmptyOption').patchValue(false, {emitEvent: false});
|
this.propertyFormGroup.get('allowEmptyOption').patchValue(false, {emitEvent: false});
|
||||||
this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false});
|
this.propertyFormGroup.get('allowEmptyOption').disable({emitEvent: false})
|
||||||
|
this.propertyFormGroup.get('minItems').enable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('maxItems').enable({emitEvent: false});
|
||||||
} else {
|
} else {
|
||||||
if (defaultValue && Array.isArray(defaultValue)) {
|
if (defaultValue && Array.isArray(defaultValue)) {
|
||||||
const newVal = defaultValue.length ? defaultValue[0] : null;
|
const newVal = defaultValue.length ? defaultValue[0] : null;
|
||||||
@ -299,6 +309,8 @@ export class DynamicFormPropertyPanelComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false});
|
this.propertyFormGroup.get('allowEmptyOption').enable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('minItems').disable({emitEvent: false});
|
||||||
|
this.propertyFormGroup.get('maxItems').disable({emitEvent: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #propertyRowTpl let-propertyRow="propertyRow">
|
<ng-template #propertyRowTpl let-propertyRow="propertyRow">
|
||||||
<div [formGroup]="propertiesFormGroup" class="tb-form-row space-between flex-wrap overflow-auto" [class]="propertyRow.rowClass">
|
<div [formGroup]="propertiesFormGroup" class="tb-form-row space-between overflow-auto" [class]="propertyRow.rowClass">
|
||||||
<mat-slide-toggle *ngIf="propertyRow.switch && propertyRow.switch.visible"
|
<mat-slide-toggle *ngIf="propertyRow.switch && propertyRow.switch.visible"
|
||||||
class="mat-slide fixed-title-width margin" formControlName="{{ propertyRow.switch.id }}">
|
class="mat-slide fixed-title-width margin" formControlName="{{ propertyRow.switch.id }}">
|
||||||
{{ propertyRow.label | customTranslate }}
|
{{ propertyRow.label | customTranslate }}
|
||||||
|
|||||||
@ -215,6 +215,16 @@ export class DynamicFormComponent implements OnInit, OnChanges, ControlValueAcce
|
|||||||
validators.push(Validators.max(property.max));
|
validators.push(Validators.max(property.max));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (property.type === FormPropertyType.select) {
|
||||||
|
if (property.multiple) {
|
||||||
|
if (isDefinedAndNotNull(property.minItems)) {
|
||||||
|
validators.push(Validators.minLength(property.minItems));
|
||||||
|
}
|
||||||
|
if (isDefinedAndNotNull(property.maxItems)) {
|
||||||
|
validators.push(Validators.maxLength(property.maxItems));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
this.propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false});
|
this.propertiesFormGroup.addControl(property.id, this.fb.control(null, validators), {emitEvent: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,6 +107,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div [class.!hidden]="selectedOption !== 'properties'" class="mat-content overflow-hidden">
|
<div [class.!hidden]="selectedOption !== 'properties'" class="mat-content overflow-hidden">
|
||||||
<tb-dynamic-form-properties
|
<tb-dynamic-form-properties
|
||||||
|
importExport
|
||||||
|
[exportFileName]="(modelValue?.title || 'scada-symbol') + '-properties'"
|
||||||
formControlName="properties">
|
formControlName="properties">
|
||||||
</tb-dynamic-form-properties>
|
</tb-dynamic-form-properties>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -90,7 +90,7 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
|
|||||||
@Input()
|
@Input()
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
|
||||||
private modelValue: ScadaSymbolMetadata;
|
modelValue: ScadaSymbolMetadata;
|
||||||
|
|
||||||
private propagateChange = null;
|
private propagateChange = null;
|
||||||
|
|
||||||
|
|||||||
@ -188,9 +188,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div #topRightPanel class="tb-split tb-content">
|
<div #topRightPanel class="tb-split tb-content">
|
||||||
<mat-tab-group mat-stretch-tabs="false" dynamicHeight="true" style="width: 100%; height: 100%;">
|
<mat-tab-group mat-stretch-tabs="false" dynamicHeight="true" style="width: 100%; height: 100%;">
|
||||||
<mat-tab label="{{ 'widget.settings-form-properties' | translate }}">
|
<mat-tab label="{{ 'widget.settings-form' | translate }}">
|
||||||
<div class="tb-resize-container" style="background-color: #fff;">
|
<div class="tb-resize-container" style="background-color: #fff;">
|
||||||
<tb-dynamic-form-properties
|
<tb-dynamic-form-properties
|
||||||
|
importExport
|
||||||
|
[exportFileName]="(widget.widgetName || 'widget') + '-settings-form'"
|
||||||
[(ngModel)]="widget.settingsForm"
|
[(ngModel)]="widget.settingsForm"
|
||||||
(ngModelChange)="settingsFormUpdated()"
|
(ngModelChange)="settingsFormUpdated()"
|
||||||
noBorder fillHeight>
|
noBorder fillHeight>
|
||||||
|
|||||||
@ -87,6 +87,7 @@ import {
|
|||||||
ExportResourceDialogData,
|
ExportResourceDialogData,
|
||||||
ExportResourceDialogDialogResult
|
ExportResourceDialogDialogResult
|
||||||
} from '@shared/import-export/export-resource-dialog.component';
|
} from '@shared/import-export/export-resource-dialog.component';
|
||||||
|
import { FormProperty, propertyValid } from '@shared/models/dynamic-form.models';
|
||||||
|
|
||||||
export type editMissingAliasesFunction = (widgets: Array<Widget>, isSingleWidget: boolean,
|
export type editMissingAliasesFunction = (widgets: Array<Widget>, isSingleWidget: boolean,
|
||||||
customTitle: string, missingEntityAliases: EntityAliases) => Observable<EntityAliases>;
|
customTitle: string, missingEntityAliases: EntityAliases) => Observable<EntityAliases>;
|
||||||
@ -119,6 +120,26 @@ export class ImportExportService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public exportFormProperties(properties: FormProperty[], fileName: string) {
|
||||||
|
this.exportToPc(properties, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public importFormProperties(): Observable<FormProperty[]> {
|
||||||
|
return this.openImportDialog('dynamic-form.import-form', 'dynamic-form.form-json-file').pipe(
|
||||||
|
map((properties: FormProperty[]) => {
|
||||||
|
if (!this.validateImportedFormProperties(properties)) {
|
||||||
|
this.store.dispatch(new ActionNotificationShow(
|
||||||
|
{message: this.translate.instant('dynamic-form.invalid-form-json-file-error'),
|
||||||
|
type: 'error'}));
|
||||||
|
throw new Error('Invalid form JSON file');
|
||||||
|
} else {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError(() => of(null))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public exportImage(type: ImageResourceType, key: string) {
|
public exportImage(type: ImageResourceType, key: string) {
|
||||||
this.imageService.exportImage(type, key).subscribe(
|
this.imageService.exportImage(type, key).subscribe(
|
||||||
{
|
{
|
||||||
@ -927,6 +948,14 @@ export class ImportExportService {
|
|||||||
type: 'error'}));
|
type: 'error'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validateImportedFormProperties(properties: FormProperty[]): boolean {
|
||||||
|
if (!properties.length) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return !properties.some(p => !propertyValid(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private validateImportedImage(image: ImageExportData): boolean {
|
private validateImportedImage(image: ImageExportData): boolean {
|
||||||
return !(!isNotEmptyStr(image.data)
|
return !(!isNotEmptyStr(image.data)
|
||||||
|| !isNotEmptyStr(image.title)
|
|| !isNotEmptyStr(image.title)
|
||||||
|
|||||||
@ -132,6 +132,8 @@ export interface FormSelectProperty extends FormPropertyBase {
|
|||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
allowEmptyOption?: boolean;
|
allowEmptyOption?: boolean;
|
||||||
items?: FormSelectItem[];
|
items?: FormSelectItem[];
|
||||||
|
minItems?: number;
|
||||||
|
maxItems?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormPropertyDirection = 'row' | 'column';
|
export type FormPropertyDirection = 'row' | 'column';
|
||||||
@ -311,6 +313,11 @@ const toPropertyContainers = (properties: FormProperty[],
|
|||||||
visible: true
|
visible: true
|
||||||
};
|
};
|
||||||
result.push(propertyRow);
|
result.push(propertyRow);
|
||||||
|
const rowClasses = (propertyRow.rowClass || '').split(' ').filter(cls => cls.trim().length > 0);
|
||||||
|
if (!rowClasses.includes('flex-wrap')) {
|
||||||
|
rowClasses.push('flex-wrap');
|
||||||
|
}
|
||||||
|
propertyRow.rowClass = rowClasses.join(' ');
|
||||||
}
|
}
|
||||||
if (property.type === FormPropertyType.switch) {
|
if (property.type === FormPropertyType.switch) {
|
||||||
propertyRow.switch = property;
|
propertyRow.switch = property;
|
||||||
@ -330,10 +337,18 @@ const toPropertyContainers = (properties: FormProperty[],
|
|||||||
delete container.properties;
|
delete container.properties;
|
||||||
delete container.rowClass;
|
delete container.rowClass;
|
||||||
} else {
|
} else {
|
||||||
if (!container.rowClass) {
|
container.propertiesRowClass = 'gt-xs:align-center xs:flex-col gt-xs:flex-row gt-xs:justify-end';
|
||||||
container.rowClass = 'column-xs';
|
const rowClasses = (container.rowClass || '').split(' ').filter(cls => cls.trim().length > 0);
|
||||||
container.propertiesRowClass = 'gt-xs:align-center xs:flex-col gt-xs:flex-row gt-xs:justify-end';
|
if (!rowClasses.includes('column-xs')) {
|
||||||
|
rowClasses.push('column-xs');
|
||||||
}
|
}
|
||||||
|
if (property.fieldClass && property.fieldClass.split(' ').includes('flex')) {
|
||||||
|
container.propertiesRowClass += ' overflow-hidden';
|
||||||
|
if (rowClasses.includes('flex-wrap')) {
|
||||||
|
rowClasses.splice(rowClasses.indexOf('flex-wrap'), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.rowClass = rowClasses.join(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -600,7 +615,7 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?:
|
|||||||
name: form.title || id,
|
name: form.title || id,
|
||||||
group: groupTitle,
|
group: groupTitle,
|
||||||
type: null,
|
type: null,
|
||||||
default: form.schema?.default || null,
|
default: (isDefinedAndNotNull(form.default) ? form.default : form.schema?.default) || null,
|
||||||
required: isDefinedAndNotNull(form.required) ? form.required : false
|
required: isDefinedAndNotNull(form.required) ? form.required : false
|
||||||
};
|
};
|
||||||
if (form.condition?.length) {
|
if (form.condition?.length) {
|
||||||
@ -635,6 +650,14 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?:
|
|||||||
property.multiple = form.multiple;
|
property.multiple = form.multiple;
|
||||||
property.fieldClass = 'flex';
|
property.fieldClass = 'flex';
|
||||||
property.allowEmptyOption = isDefinedAndNotNull(form.allowClear) ? form.allowClear : false;
|
property.allowEmptyOption = isDefinedAndNotNull(form.allowClear) ? form.allowClear : false;
|
||||||
|
if (property.multiple) {
|
||||||
|
if (typeof (form.schema as any)?.minItems === 'number') {
|
||||||
|
property.minItems = (form.schema as any).minItems;
|
||||||
|
}
|
||||||
|
if (typeof (form.schema as any)?.maxItems === 'number') {
|
||||||
|
property.maxItems = (form.schema as any).maxItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'select':
|
case 'select':
|
||||||
property.type = FormPropertyType.select;
|
property.type = FormPropertyType.select;
|
||||||
@ -699,17 +722,25 @@ const jsonFormDataToProperty = (form: JsonFormData, level: number, groupTitle?:
|
|||||||
break;
|
break;
|
||||||
case 'array':
|
case 'array':
|
||||||
if (form.items?.length) {
|
if (form.items?.length) {
|
||||||
const item: JsonFormData = form.items[0] as JsonFormData;
|
const arrayItemSchema = form.schema.items;
|
||||||
if (item.type !== 'array') {
|
if (arrayItemSchema && arrayItemSchema.type && arrayItemSchema.type !== 'array') {
|
||||||
const arrayProperty = jsonFormDataToProperty(item, 0);
|
if (arrayItemSchema.type === 'object') {
|
||||||
arrayProperty.arrayItemType = arrayProperty.type;
|
property.arrayItemType = FormPropertyType.fieldset;
|
||||||
arrayProperty.arrayItemName = arrayProperty.name;
|
property.arrayItemName = '';
|
||||||
arrayProperty.id = property.id;
|
property.properties = form.items ? (form.items as JsonFormData[]).map(item =>
|
||||||
arrayProperty.name = property.name;
|
jsonFormDataToProperty(item, level+2)).filter(p => p !== null) : [];
|
||||||
arrayProperty.group = property.group;
|
} else {
|
||||||
arrayProperty.condition = property.condition;
|
const item: JsonFormData = form.items[0] as JsonFormData;
|
||||||
arrayProperty.required = property.required;
|
const arrayProperty = jsonFormDataToProperty(item, 0);
|
||||||
property = arrayProperty;
|
arrayProperty.arrayItemType = arrayProperty.type;
|
||||||
|
arrayProperty.arrayItemName = arrayProperty.name;
|
||||||
|
arrayProperty.id = property.id;
|
||||||
|
arrayProperty.name = property.name;
|
||||||
|
arrayProperty.group = property.group;
|
||||||
|
arrayProperty.condition = property.condition;
|
||||||
|
arrayProperty.required = property.required;
|
||||||
|
property = arrayProperty;
|
||||||
|
}
|
||||||
property.type = FormPropertyType.array;
|
property.type = FormPropertyType.array;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1657,6 +1657,7 @@
|
|||||||
"min": "Min",
|
"min": "Min",
|
||||||
"max": "Max",
|
"max": "Max",
|
||||||
"step": "Step",
|
"step": "Step",
|
||||||
|
"selected-options-limit": "Selected options limit",
|
||||||
"advanced-ui-settings": "Advanced UI settings",
|
"advanced-ui-settings": "Advanced UI settings",
|
||||||
"disable-on-property": "Disable on property",
|
"disable-on-property": "Disable on property",
|
||||||
"display-condition-function": "Display condition function",
|
"display-condition-function": "Display condition function",
|
||||||
@ -1693,7 +1694,13 @@
|
|||||||
"item-type": "Item type",
|
"item-type": "Item type",
|
||||||
"item-name": "Item name",
|
"item-name": "Item name",
|
||||||
"no-items": "No items"
|
"no-items": "No items"
|
||||||
}
|
},
|
||||||
|
"clear-form": "Clear form",
|
||||||
|
"clear-form-prompt": "Are you sure you want to remove all form properties?",
|
||||||
|
"import-form": "Import form from JSON",
|
||||||
|
"export-form": "Export form to JSON",
|
||||||
|
"form-json-file": "Form JSON file",
|
||||||
|
"invalid-form-json-file-error": "Unable to import form from JSON: Invalid form JSON data structure."
|
||||||
},
|
},
|
||||||
"asset-profile": {
|
"asset-profile": {
|
||||||
"asset-profile": "Asset profile",
|
"asset-profile": "Asset profile",
|
||||||
@ -5473,7 +5480,7 @@
|
|||||||
"html": "HTML",
|
"html": "HTML",
|
||||||
"tidy": "Tidy",
|
"tidy": "Tidy",
|
||||||
"css": "CSS",
|
"css": "CSS",
|
||||||
"settings-form-properties": "Settings form properties",
|
"settings-form": "Settings form",
|
||||||
"settings-schema": "Settings schema",
|
"settings-schema": "Settings schema",
|
||||||
"datakey-settings-schema": "Data key settings schema",
|
"datakey-settings-schema": "Data key settings schema",
|
||||||
"latest-datakey-settings-schema": "Latest data key settings schema",
|
"latest-datakey-settings-schema": "Latest data key settings schema",
|
||||||
|
|||||||
@ -223,7 +223,7 @@
|
|||||||
}
|
}
|
||||||
.mat-divider-vertical {
|
.mat-divider-vertical {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
margin-top: -7px;
|
margin-top: -9px;
|
||||||
margin-bottom: -7px;
|
margin-bottom: -7px;
|
||||||
}
|
}
|
||||||
.mat-mdc-form-field, tb-unit-input {
|
.mat-mdc-form-field, tb-unit-input {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user