Browse Source

UI: SCADA symbol preview mode.

pull/11391/head
Igor Kulikov 2 years ago
parent
commit
32d34e0acc
  1. 5
      ui-ngx/src/app/core/utils.ts
  2. 2
      ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg-widget.component.html
  3. 39
      ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg-widget.component.ts
  4. 4
      ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg-widget.models.ts
  5. 87
      ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg.models.ts
  6. 44
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.html
  7. 150
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.ts
  8. 63
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts

5
ui-ngx/src/app/core/utils.ts

@ -229,6 +229,11 @@ export const updateFileContent = (file: File, newContent: string): File => {
return new File([blob], file.name, {type: file.type}); return new File([blob], file.name, {type: file.type});
}; };
export const createFileFromContent = (content: string, name: string, type: string): File => {
const blob = new Blob([content], { type });
return new File([blob], name, { type });
};
const scrollRegex = /(auto|scroll)/; const scrollRegex = /(auto|scroll)/;
function parentNodes(node: Node, nodes: Node[]): Node[] { function parentNodes(node: Node, nodes: Node[]): Node[] {

2
ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg-widget.component.html

@ -22,5 +22,5 @@
<div #iotSvgShape class="tb-iot-svg-shape"> <div #iotSvgShape class="tb-iot-svg-shape">
</div> </div>
</div> </div>
<mat-progress-bar class="tb-action-widget-progress" style="height: 4px;" color="accent" mode="indeterminate" *ngIf="iotSvgObject.loading$ | async"></mat-progress-bar> <mat-progress-bar class="tb-action-widget-progress" style="height: 4px;" color="accent" mode="indeterminate" *ngIf="iotSvgObject?.loading$ | async"></mat-progress-bar>
</div> </div>

39
ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg-widget.component.ts

@ -35,8 +35,9 @@ import {
iotSvgWidgetDefaultSettings, iotSvgWidgetDefaultSettings,
IotSvgWidgetSettings IotSvgWidgetSettings
} from '@home/components/widget/lib/svg/iot-svg-widget.models'; } from '@home/components/widget/lib/svg/iot-svg-widget.models';
import { Observable } from 'rxjs'; import { Observable, of } from 'rxjs';
import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models';
import { ImageService } from '@core/http/image.service';
@Component({ @Component({
selector: 'tb-iot-svg-widget', selector: 'tb-iot-svg-widget',
@ -63,6 +64,7 @@ export class IotSvgWidgetComponent extends
constructor(protected imagePipe: ImagePipe, constructor(protected imagePipe: ImagePipe,
protected sanitizer: DomSanitizer, protected sanitizer: DomSanitizer,
private renderer: Renderer2, private renderer: Renderer2,
private imageService: ImageService,
protected cd: ChangeDetectorRef, protected cd: ChangeDetectorRef,
private http: HttpClient) { private http: HttpClient) {
super(cd); super(cd);
@ -75,15 +77,40 @@ export class IotSvgWidgetComponent extends
this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer);
this.overlayStyle = overlayStyle(this.settings.background.overlay); this.overlayStyle = overlayStyle(this.settings.background.overlay);
this.iotSvgObject = new IotSvgObject(this.ctx, this.settings.iotSvg, this.settings.iotSvgObject); let svgContent$: Observable<string>;
if (this.settings.iotSvgContent) {
svgContent$ = of(this.settings.iotSvgContent);
} else if (this.settings.iotSvgUrl) {
svgContent$ = this.imageService.getImageString(this.settings.iotSvgUrl);
} else {
svgContent$ = this.http.get(this.settings.iotSvg, {responseType: 'text'});
}
svgContent$.subscribe(
(content) => {
this.initObject(content);
}
);
}
private initObject(svgContent: string) {
this.iotSvgObject = new IotSvgObject(this.ctx, svgContent, this.settings.iotSvgObject);
this.iotSvgObject.onError((error) => { this.iotSvgObject.onError((error) => {
this.ctx.showErrorToast(error, 'bottom', 'center', this.ctx.toastTargetId, true); this.ctx.showErrorToast(error, 'bottom', 'center', this.ctx.toastTargetId, true);
}); });
this.iotSvgObject.init().subscribe(); this.iotSvgObject.init();
if (this.iotSvgShape) {
this.iotSvgObject.addTo(this.iotSvgShape.nativeElement);
if (this.autoScale) {
this.onResize();
}
}
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.iotSvgObject.addTo(this.iotSvgShape.nativeElement); if (this.iotSvgObject) {
this.iotSvgObject.addTo(this.iotSvgShape.nativeElement);
}
if (this.autoScale) { if (this.autoScale) {
this.shapeResize$ = new ResizeObserver(() => { this.shapeResize$ = new ResizeObserver(() => {
this.onResize(); this.onResize();
@ -114,6 +141,8 @@ export class IotSvgWidgetComponent extends
private onResize() { private onResize() {
const shapeWidth = this.iotSvgShape.nativeElement.getBoundingClientRect().width; const shapeWidth = this.iotSvgShape.nativeElement.getBoundingClientRect().width;
const shapeHeight = this.iotSvgShape.nativeElement.getBoundingClientRect().height; const shapeHeight = this.iotSvgShape.nativeElement.getBoundingClientRect().height;
this.iotSvgObject.setSize(shapeWidth, shapeHeight); if (this.iotSvgObject) {
this.iotSvgObject.setSize(shapeWidth, shapeHeight);
}
} }
} }

4
ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg-widget.models.ts

@ -18,7 +18,9 @@ import { IotSvgObjectSettings } from '@home/components/widget/lib/svg/iot-svg.mo
import { BackgroundSettings, BackgroundType } from '@shared/models/widget-settings.models'; import { BackgroundSettings, BackgroundType } from '@shared/models/widget-settings.models';
export interface IotSvgWidgetSettings { export interface IotSvgWidgetSettings {
iotSvg: string; iotSvg?: string;
iotSvgUrl?: string;
iotSvgContent?: string;
iotSvgObject: IotSvgObjectSettings; iotSvgObject: IotSvgObjectSettings;
background: BackgroundSettings; background: BackgroundSettings;
} }

87
ui-ngx/src/app/modules/home/components/widget/lib/svg/iot-svg.models.ts

@ -34,7 +34,7 @@ import {
parseFunction parseFunction
} from '@core/utils'; } from '@core/utils';
import { BehaviorSubject, forkJoin, Observable, Observer } from 'rxjs'; import { BehaviorSubject, forkJoin, Observable, Observer } from 'rxjs';
import { map, share } from 'rxjs/operators'; import { share } from 'rxjs/operators';
import { ValueAction, ValueGetter, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; import { ValueAction, ValueGetter, ValueSetter } from '@home/components/widget/lib/action/action-widget.models';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { ColorProcessor, constantColor, Font } from '@shared/models/widget-settings.models'; import { ColorProcessor, constantColor, Font } from '@shared/models/widget-settings.models';
@ -196,45 +196,38 @@ const updateIotSvgMetadataInDom = (svgDoc: Document, metadata: IotSvgMetadata) =
}; };
const svgPartsRegex = /(<svg .*?>)(.*)<\/svg>/gms; const svgPartsRegex = /(<svg .*?>)(.*)<\/svg>/gms;
const tbMetadataRegex = /<tb:metadata>.*<\/tb:metadata>/gs;
const svgMetadataRegex = /<tb:metadata>.*<\/tb:metadata>/gs; export interface IoTSvgContentData {
svgRootNode: string;
export const svgRootNodePart = (svgContent: string): string => { innerSvg: string;
let result = svgContent; }
svgPartsRegex.lastIndex = 0;
const match = svgPartsRegex.exec(svgContent);
if (match != null && match.length > 1) {
result = match[1];
}
return result;
};
export const innerSvgContent = (svgContent: string): string => { export const iotSvgContentData = (svgContent: string): IoTSvgContentData => {
let result = svgContent; const result: IoTSvgContentData = {
svgRootNode: '',
innerSvg: ''
};
svgPartsRegex.lastIndex = 0; svgPartsRegex.lastIndex = 0;
const match = svgPartsRegex.exec(svgContent); const match = svgPartsRegex.exec(svgContent);
if (match != null && match.length > 2) { if (match != null) {
result = match[2]; if (match.length > 1) {
} result.svgRootNode = match[1];
return result; }
}; if (match.length > 2) {
let innerSvgContent = match[2];
export const stripSvgMetadata = (svgContent: string) => { tbMetadataRegex.lastIndex = 0;
let result = svgContent; const metadataMatch = tbMetadataRegex.exec(svgContent);
svgMetadataRegex.lastIndex = 0; if (metadataMatch !== null && metadataMatch.length) {
const match = svgMetadataRegex.exec(svgContent); const metadata = metadataMatch[0];
if (match !== null && match.length) { innerSvgContent = innerSvgContent.replace(metadata, '');
const metadata = match[0]; }
result = result.replace(metadata, ''); result.innerSvg = innerSvgContent;
}
} }
return result; return result;
}; };
export const innerSvgContentFromSvgjs = (svgContent: string): string => {
const result = innerSvgContent(svgContent);
return result.replace(/svgjs:.*?=".*?"/gs, '');
};
const defaultGetValueSettings = (get: IotSvgBehaviorValue): GetValueSettings<any> => ({ const defaultGetValueSettings = (get: IotSvgBehaviorValue): GetValueSettings<any> => ({
action: GetValueAction.DO_NOTHING, action: GetValueAction.DO_NOTHING,
defaultValue: get.defaultValue, defaultValue: get.defaultValue,
@ -335,21 +328,18 @@ export class IotSvgObject {
private _onError: (error: string) => void = () => {}; private _onError: (error: string) => void = () => {};
constructor(private ctx: WidgetContext, constructor(private ctx: WidgetContext,
private svgPath: string, private svgContent: string,
private inputSettings: IotSvgObjectSettings) {} private inputSettings: IotSvgObjectSettings) {
}
public init(): Observable<any> {
return this.ctx.http.get(this.svgPath, {responseType: 'text'}).pipe( public init() {
map((inputSvgContent) => { const doc: XMLDocument = new DOMParser().parseFromString(this.svgContent, 'image/svg+xml');
const doc: XMLDocument = new DOMParser().parseFromString(inputSvgContent, 'image/svg+xml'); this.metadata = parseIotSvgMetadataFromDom(doc);
this.metadata = parseIotSvgMetadataFromDom(doc); const defaults = defaultIotSvgObjectSettings(this.metadata);
const defaults = defaultIotSvgObjectSettings(this.metadata); this.settings = mergeDeep<IotSvgObjectSettings>({}, defaults, this.inputSettings || {});
this.settings = mergeDeep<IotSvgObjectSettings>({}, defaults, this.inputSettings || {}); this.prepareMetadata();
this.prepareMetadata(); this.prepareSvgShape(doc);
this.prepareSvgShape(doc); this.initialize();
this.initialize();
})
);
} }
public onError(onError: (error: string) => void) { public onError(onError: (error: string) => void) {
@ -371,6 +361,9 @@ export class IotSvgObject {
this.valueActions.forEach(v => v.destroy()); this.valueActions.forEach(v => v.destroy());
this.loadingSubject.complete(); this.loadingSubject.complete();
this.loadingSubject.unsubscribe(); this.loadingSubject.unsubscribe();
if (this.svgShape) {
this.svgShape.remove();
}
} }
public setSize(targetWidth: number, targetHeight: number) { public setSize(targetWidth: number, targetHeight: number) {

44
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.html

@ -29,41 +29,39 @@
[showCloseDetails]="false" [showCloseDetails]="false"
headerHeightPx="64" headerHeightPx="64"
headerTitle="{{symbolData?.imageResource?.title}}"> headerTitle="{{symbolData?.imageResource?.title}}">
<div [formGroup]="scadaSymbolFormGroup"> <button mat-button
*ngIf="previewMode"
(click)="exitPreviewMode()">
<mat-icon>chevron_left</mat-icon>
{{ 'action.back' | translate }}
</button>
<div [fxShow]="!previewMode" [formGroup]="scadaSymbolFormGroup">
<tb-scada-symbol-metadata formControlName="metadata"> <tb-scada-symbol-metadata formControlName="metadata">
<div class="tb-scada-symbol-metadata-header-prefix"> <div class="tb-scada-symbol-metadata-header-prefix">
<button mat-button <button fxHide.gt-sm
*ngIf="previewMode"
(click)="previewMode = false">
<mat-icon>chevron_left</mat-icon>
{{ 'action.back' | translate }}
</button>
<button *ngIf="!previewMode"
fxHide.gt-sm
[disabled]="!scadaSymbolFormGroup.valid" [disabled]="!scadaSymbolFormGroup.valid"
mat-button color="primary" mat-button color="primary"
(click)="previewMode = true"> (click)="enterPreviewMode()">
<mat-icon>visibility</mat-icon> <mat-icon>visibility</mat-icon>
{{ 'scada.preview' | translate }} {{ 'scada.preview' | translate }}
</button> </button>
</div> </div>
<div fxFlex.lt-md class="tb-scada-symbol-metadata-header-suffix" fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="12px"> <div fxFlex.lt-md class="tb-scada-symbol-metadata-header-suffix" fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="12px">
<button *ngIf="!previewMode" <button fxHide.lt-md
fxHide.lt-md
[disabled]="!scadaSymbolFormGroup.valid" [disabled]="!scadaSymbolFormGroup.valid"
mat-button color="primary" mat-button color="primary"
(click)="previewMode = true"> (click)="enterPreviewMode()">
<mat-icon>visibility</mat-icon> <mat-icon>visibility</mat-icon>
{{ 'widget-config.preview' | translate }} {{ 'scada.preview' | translate }}
</button> </button>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="12px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="12px">
<button [disabled]="!(scadaSymbolFormGroup.dirty || symbolEditor.dirty)" <button [disabled]="!(scadaSymbolFormGroup.dirty || symbolEditor?.dirty)"
mat-stroked-button color="primary" mat-stroked-button color="primary"
(click)="onRevertScadaSymbolConfig()"> (click)="onRevertScadaSymbolConfig()">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
{{ 'action.decline' | translate }} {{ 'action.decline' | translate }}
</button> </button>
<button [disabled]="!scadaSymbolFormGroup.valid || !(scadaSymbolFormGroup.dirty || symbolEditor.dirty)" <button [disabled]="!scadaSymbolFormGroup.valid || !(scadaSymbolFormGroup.dirty || symbolEditor?.dirty)"
mat-flat-button color="primary" mat-flat-button color="primary"
(click)="onApplyScadaSymbolConfig()"> (click)="onApplyScadaSymbolConfig()">
<mat-icon>done</mat-icon> <mat-icon>done</mat-icon>
@ -76,7 +74,19 @@
</tb-details-panel> </tb-details-panel>
</mat-drawer> </mat-drawer>
<mat-drawer-content class="tb-scada-symbol-content"> <mat-drawer-content class="tb-scada-symbol-content">
<tb-scada-symbol-editor #symbolEditor [data]="symbolEditorData"></tb-scada-symbol-editor> <tb-scada-symbol-editor *ngIf="!previewMode" #symbolEditor [data]="symbolEditorData"></tb-scada-symbol-editor>
<tb-dashboard *ngIf="previewMode"
class="tb-absolute-fill"
[aliasController]="aliasController"
[widgets]="previewWidgets"
[autofillHeight]="true"
[columns]="24"
[margin]="0"
[isEdit]="false"
[isMobileDisabled]="true"
[isEditActionEnabled]="false"
[isRemoveActionEnabled]="false">
</tb-dashboard>
</mat-drawer-content> </mat-drawer-content>
</mat-drawer-container> </mat-drawer-container>
</div> </div>

150
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.ts

@ -18,6 +18,7 @@ import {
AfterViewInit, AfterViewInit,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
OnDestroy, OnDestroy,
OnInit, OnInit,
@ -28,16 +29,35 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { takeUntil } from 'rxjs/operators'; import { map, switchMap, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { ScadaSymbolData } from '@home/pages/scada-symbol/scada-symbol.models'; import { ScadaSymbolData } from '@home/pages/scada-symbol/scada-symbol.models';
import { IotSvgMetadata, parseIotSvgMetadataFromContent } from '@home/components/widget/lib/svg/iot-svg.models'; import {
IotSvgMetadata, IotSvgObjectSettings,
parseIotSvgMetadataFromContent,
updateIotSvgMetadataInContent
} from '@home/components/widget/lib/svg/iot-svg.models';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { deepClone } from '@core/utils'; import { createFileFromContent, deepClone } from '@core/utils';
import { import {
ScadaSymbolEditorComponent, ScadaSymbolEditorComponent,
ScadaSymbolEditorData ScadaSymbolEditorData
} from '@home/pages/scada-symbol/scada-symbol-editor.component'; } from '@home/pages/scada-symbol/scada-symbol-editor.component';
import { ImageService } from '@core/http/image.service';
import { imageResourceType, IMAGES_URL_PREFIX } from '@shared/models/resource.models';
import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard';
import { IAliasController, IStateController, StateParams } from '@core/api/widget-api.models';
import { EntityAliases } from '@shared/models/alias.models';
import { Filters } from '@shared/models/query/query.models';
import { AliasController } from '@core/api/alias-controller';
import { EntityService } from '@core/http/entity.service';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
import { Widget, widgetType } from '@shared/models/widget.models';
import {
iotSvgWidgetDefaultSettings,
IotSvgWidgetSettings
} from '@home/components/widget/lib/svg/iot-svg-widget.models';
@Component({ @Component({
selector: 'tb-scada-symbol', selector: 'tb-scada-symbol',
@ -45,17 +65,16 @@ import {
styleUrls: ['./scada-symbol.component.scss'], styleUrls: ['./scada-symbol.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDestroy, AfterViewInit { export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDestroy, AfterViewInit, HasDirtyFlag {
@HostBinding('style.width') width = '100%'; @HostBinding('style.width') width = '100%';
@HostBinding('style.height') height = '100%'; @HostBinding('style.height') height = '100%';
@ViewChild('symbolEditor') @ViewChild('symbolEditor', {static: false})
symbolEditor: ScadaSymbolEditorComponent; symbolEditor: ScadaSymbolEditorComponent;
symbolData: ScadaSymbolData; symbolData: ScadaSymbolData;
symbolEditorData: ScadaSymbolEditorData; symbolEditorData: ScadaSymbolEditorData;
metadata: IotSvgMetadata;
previewMode = false; previewMode = false;
@ -65,11 +84,33 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
private origSymbolData: ScadaSymbolData; private origSymbolData: ScadaSymbolData;
updateBreadcrumbs = new EventEmitter();
aliasController: IAliasController;
previewWidgets: Array<Widget> = [];
private previewWidget: Widget;
private forcePristine = false;
get isDirty(): boolean {
return (this.scadaSymbolFormGroup.dirty || this.symbolEditor?.dirty) && !this.forcePristine;
}
set isDirty(value: boolean) {
this.forcePristine = !value;
}
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private entityService: EntityService,
private utils: UtilsService,
private translate: TranslateService,
private imageService: ImageService) {
super(store); super(store);
} }
@ -77,6 +118,21 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
this.scadaSymbolFormGroup = this.fb.group({ this.scadaSymbolFormGroup = this.fb.group({
metadata: [null] metadata: [null]
}); });
const entitiAliases: EntityAliases = {};
// @ts-ignore
const stateController: IStateController = {
getStateParams: (): StateParams => ({})
};
const filters: Filters = {};
this.aliasController = new AliasController(this.utils,
this.entityService,
this.translate,
() => stateController, entitiAliases, filters);
this.route.data.pipe( this.route.data.pipe(
takeUntil(this.destroy$) takeUntil(this.destroy$)
).subscribe( ).subscribe(
@ -98,8 +154,33 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
onApplyScadaSymbolConfig() { onApplyScadaSymbolConfig() {
if (this.scadaSymbolFormGroup.valid) { if (this.scadaSymbolFormGroup.valid) {
const svgContent = this.symbolEditor.getContent(); const svgContent = this.prepareSvgContent();
console.log(svgContent); const file = createFileFromContent(svgContent, this.symbolData.imageResource.fileName,
this.symbolData.imageResource.descriptor.mediaType);
const type = imageResourceType(this.symbolData.imageResource);
let imageInfoObservable =
this.imageService.updateImage(type, this.symbolData.imageResource.resourceKey, file);
const metadata: IotSvgMetadata = this.scadaSymbolFormGroup.get('metadata').value;
if (metadata.title !== this.symbolData.imageResource.title) {
imageInfoObservable = imageInfoObservable.pipe(
switchMap(imageInfo => {
imageInfo.title = metadata.title;
return this.imageService.updateImageInfo(imageInfo);
})
);
}
imageInfoObservable.pipe(
switchMap(imageInfo => this.imageService.getImageString(
`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(imageInfo.resourceKey)}`).pipe(
map(content => ({
imageResource: imageInfo,
svgContent: content
}))
))
).subscribe(data => {
this.init(data);
this.updateBreadcrumbs.emit();
});
} }
} }
@ -107,6 +188,53 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
this.init(this.origSymbolData); this.init(this.origSymbolData);
} }
enterPreviewMode() {
this.symbolData.svgContent = this.prepareSvgContent();
const iotSvgObject: IotSvgObjectSettings = {};
const settings: IotSvgWidgetSettings = {...iotSvgWidgetDefaultSettings,
...{iotSvg: null, iotSvgContent: this.symbolData.svgContent, iotSvgObject}};
this.previewWidget = {
typeFullFqn: 'system.iot_svg',
type: widgetType.rpc,
sizeX: 24,
sizeY: 24,
row: 0,
col: 0,
config: {
settings,
dropShadow: false,
padding: '0',
margin: '0'
}
};
this.previewWidgets = [this.previewWidget];
this.previewMode = true;
/*setTimeout(() => {
this.updatePreviewWidgetSettings();
}, 2000);*/
}
exitPreviewMode() {
this.symbolEditorData = {
svgContent: this.symbolData.svgContent
};
this.previewMode = false;
}
private updatePreviewWidgetSettings() {
this.previewWidget = deepClone(this.previewWidget);
this.previewWidget.config.title = this.symbolData.imageResource.title;
this.previewWidget.config.showTitle = true;
this.previewWidget.config.padding = '48px';
this.previewWidgets = [this.previewWidget];
}
private prepareSvgContent(): string {
const svgContent = this.symbolEditor.getContent();
const metadata: IotSvgMetadata = this.scadaSymbolFormGroup.get('metadata').value;
return updateIotSvgMetadataInContent(svgContent, metadata);
}
private reset(): void { private reset(): void {
this.previewMode = false; this.previewMode = false;
} }
@ -117,9 +245,9 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
this.symbolEditorData = { this.symbolEditorData = {
svgContent: this.symbolData.svgContent svgContent: this.symbolData.svgContent
}; };
this.metadata = parseIotSvgMetadataFromContent(this.symbolData.svgContent); const metadata = parseIotSvgMetadataFromContent(this.symbolData.svgContent);
this.scadaSymbolFormGroup.patchValue({ this.scadaSymbolFormGroup.patchValue({
metadata: this.metadata metadata
}, {emitEvent: false}); }, {emitEvent: false});
this.scadaSymbolFormGroup.markAsPristine(); this.scadaSymbolFormGroup.markAsPristine();
this.cd.markForCheck(); this.cd.markForCheck();

63
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts

@ -18,24 +18,17 @@ import { ImageResourceInfo } from '@shared/models/resource.models';
import * as svgjs from '@svgdotjs/svg.js'; import * as svgjs from '@svgdotjs/svg.js';
import { Box, Element, Rect, Style, SVG, Svg, Timeline } from '@svgdotjs/svg.js'; import { Box, Element, Rect, Style, SVG, Svg, Timeline } from '@svgdotjs/svg.js';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service'; import { ViewContainerRef } from '@angular/core';
import { Directive, ElementRef, OnDestroy, Type, ViewContainerRef } from '@angular/core';
import { forkJoin, from } from 'rxjs'; import { forkJoin, from } from 'rxjs';
import { TbInject } from '@shared/decorators/tb-inject';
import { import {
setupAddTagPanelTooltip, setupAddTagPanelTooltip,
setupTagPanelTooltip setupTagPanelTooltip
} from '@home/pages/scada-symbol/scada-symbol-tooltip.components'; } from '@home/pages/scada-symbol/scada-symbol-tooltip.components';
import { iotSvgContentData } from '@home/components/widget/lib/svg/iot-svg.models';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide; import TooltipPositioningSide = JQueryTooltipster.TooltipPositioningSide;
import ITooltipsterHelper = JQueryTooltipster.ITooltipsterHelper; import ITooltipsterHelper = JQueryTooltipster.ITooltipsterHelper;
import ITooltipPosition = JQueryTooltipster.ITooltipPosition; import ITooltipPosition = JQueryTooltipster.ITooltipPosition;
import {
innerSvgContent,
innerSvgContentFromSvgjs,
stripSvgMetadata,
svgRootNodePart
} from '@home/components/widget/lib/svg/iot-svg.models';
export interface ScadaSymbolData { export interface ScadaSymbolData {
imageResource: ImageResourceInfo; imageResource: ImageResourceInfo;
@ -69,18 +62,15 @@ export class ScadaSymbolEditObject {
} }
this.scale = 1; this.scale = 1;
this.dirty = false; this.dirty = false;
this.svgRootNodePart = svgRootNodePart(svgContent); const contentData = iotSvgContentData(svgContent);
svgContent = innerSvgContent(svgContent); this.svgRootNodePart = contentData.svgRootNode;
svgContent = stripSvgMetadata(svgContent); this.svgShape = SVG().addTo(this.rootElement).svg(contentData.innerSvg);
console.log(svgContent);
// const doc: XMLDocument = new DOMParser().parseFromString(svgContent, 'image/svg+xml');
this.svgShape = SVG().addTo(this.rootElement).svg(svgContent);
this.svgShape.node.style.overflow = 'visible'; this.svgShape.node.style.overflow = 'visible';
this.svgShape.node.style['user-select'] = 'none'; this.svgShape.node.style['user-select'] = 'none';
this.box = this.svgShape.bbox(); this.box = this.svgShape.bbox();
this.svgShape.size(this.box.width, this.box.height); this.svgShape.size(this.box.width, this.box.height);
this.svgShape.viewbox(`0 0 ${this.box.width} ${this.box.height}`); this.svgShape.viewbox(`0 0 ${this.box.width} ${this.box.height}`);
this.svgShape.style().rule('.tb-element', {cursor: 'pointer', transition: '0.2s filter ease-in-out'}); this.svgShape.style().attr('tb:inner', true).rule('.tb-element', {cursor: 'pointer', transition: '0.2s filter ease-in-out'});
this.updateHoverFilterStyle(); this.updateHoverFilterStyle();
this.performSetup = true; this.performSetup = true;
this.shapeResize$.observe(this.rootElement); this.shapeResize$.observe(this.rootElement);
@ -88,17 +78,18 @@ export class ScadaSymbolEditObject {
public getContent(): string { public getContent(): string {
if (this.svgShape) { if (this.svgShape) {
let svgContent = innerSvgContentFromSvgjs(this.svgShape.svg()); const svgContent = this.svgShape.svg((e: Element) => {
console.log(svgContent); if (e.node.hasAttribute('tb:inner')) {
svgContent = `${this.svgRootNodePart}\n${svgContent}\n</svg>`; return false;
const doc: XMLDocument = new DOMParser().parseFromString(svgContent, 'image/svg+xml'); } else {
const root = $(doc.documentElement); e.node.classList.remove('tb-element', 'tooltipstered');
root.find('.tb-element').removeClass('tb-element').each((i, e) => { if (!e.node.classList.length) {
// e.node.removeAttribute('class');
}); }
root.find('.tooltipstered').removeClass('tooltipstered'); e.attr('svgjs:data', null);
svgContent = doc.documentElement.outerHTML; }
return svgContent; }, false);
return `${this.svgRootNodePart}\n${svgContent}\n</svg>`;
} else { } else {
return null; return null;
} }
@ -270,7 +261,7 @@ export class ScadaSymbolEditObject {
const whiteBlur = (2.8 / this.scale).toFixed(2); const whiteBlur = (2.8 / this.scale).toFixed(2);
const blackBlur = (1.2 / this.scale).toFixed(2); const blackBlur = (1.2 / this.scale).toFixed(2);
this.hoverFilterStyle = this.hoverFilterStyle =
this.svgShape.style().rule('.hovered', this.svgShape.style().attr('tb:inner', true).rule('.hovered',
{ {
filter: filter:
`drop-shadow(0px 0px ${whiteBlur}px white) drop-shadow(0px 0px ${whiteBlur}px white) `drop-shadow(0px 0px ${whiteBlur}px white) drop-shadow(0px 0px ${whiteBlur}px white)
@ -358,6 +349,7 @@ export class ScadaSymbolElement {
.x(this.box.x - groupRectPadding) .x(this.box.x - groupRectPadding)
.y(this.box.y - groupRectPadding) .y(this.box.y - groupRectPadding)
.attr({ .attr({
'tb:inner': true,
fill: 'none', fill: 'none',
rx: this.unscaled(6), rx: this.unscaled(6),
stroke: 'rgba(0, 0, 0, 0.38)', stroke: 'rgba(0, 0, 0, 0.38)',
@ -682,18 +674,3 @@ export class ScadaSymbolElement {
} }
} }
@Directive()
class TbTooltipContent implements OnDestroy {
componentType: Type<any>;
dynamicComponentFactoryService: DynamicComponentFactoryService;
[key: string]: any;
constructor(@TbInject(ElementRef<HTMLElement>) public element: ElementRef<HTMLElement>) {
}
ngOnDestroy(): void {
this.dynamicComponentFactoryService.destroyDynamicComponent(this.componentType);
}
}

Loading…
Cancel
Save