Browse Source

UI: Symbol editor tags edit.

pull/11391/head
Igor Kulikov 2 years ago
parent
commit
68e8f32f32
  1. 10
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.html
  2. 52
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.ts
  3. 3
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.scss
  4. 66
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts
  5. 3
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html
  6. 4
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.scss
  7. 22
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts
  8. 18
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.ts
  9. 3
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-tooltip.component.scss
  10. 119
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-tooltip.components.ts
  11. 7
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.html
  12. 55
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.component.ts
  13. 74
      ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol.models.ts
  14. 5
      ui-ngx/src/app/shared/components/js-func.component.ts

10
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.html

@ -40,14 +40,15 @@
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div class="tb-form-panel stroked"> <div class="tb-form-panel stroked">
<mat-expansion-panel class="tb-settings"> <mat-expansion-panel #renderFunctionExpansionPanel class="tb-settings">
<mat-expansion-panel-header fxLayout="row wrap"> <mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title> <mat-panel-title>
{{ 'scada.state-render-function' | translate }} {{ 'scada.state-render-function' | translate }}
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="stateRenderFunction" <tb-js-func #stateRenderFunction
formControlName="stateRenderFunction"
[functionArgs]="['element', 'ctx']" [functionArgs]="['element', 'ctx']"
helpId="widget/lib/timeseries/cell_style_fn"> helpId="widget/lib/timeseries/cell_style_fn">
</tb-js-func> </tb-js-func>
@ -55,14 +56,15 @@
</mat-expansion-panel> </mat-expansion-panel>
</div> </div>
<div class="tb-form-panel stroked"> <div class="tb-form-panel stroked">
<mat-expansion-panel class="tb-settings"> <mat-expansion-panel #clickActionExpansionPanel class="tb-settings">
<mat-expansion-panel-header fxLayout="row wrap"> <mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title> <mat-panel-title>
{{ 'scada.on-click-action' | translate }} {{ 'scada.on-click-action' | translate }}
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-js-func formControlName="clickAction" <tb-js-func #clickAction
formControlName="clickAction"
[functionArgs]="['event', 'ctx']" [functionArgs]="['event', 'ctx']"
helpId="widget/lib/timeseries/cell_style_fn"> helpId="widget/lib/timeseries/cell_style_fn">
</tb-js-func> </tb-js-func>

52
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component.ts

@ -16,12 +16,15 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, EventEmitter, Component,
EventEmitter,
forwardRef, forwardRef,
Input, Input,
OnChanges, OnChanges,
OnInit, Output, OnInit,
SimpleChanges, ViewChild, Output,
SimpleChanges,
ViewChild,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { import {
@ -33,6 +36,7 @@ import {
} from '@angular/forms'; } from '@angular/forms';
import { IotSvgTag } from '@home/components/widget/lib/svg/iot-svg.models'; import { IotSvgTag } from '@home/components/widget/lib/svg/iot-svg.models';
import { MatExpansionPanel } from '@angular/material/expansion'; import { MatExpansionPanel } from '@angular/material/expansion';
import { JsFuncComponent } from '@shared/components/js-func.component';
@Component({ @Component({
selector: 'tb-scada-symbol-metadata-tag', selector: 'tb-scada-symbol-metadata-tag',
@ -52,6 +56,18 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On
@ViewChild('expansionPanel') @ViewChild('expansionPanel')
expansionPanel: MatExpansionPanel; expansionPanel: MatExpansionPanel;
@ViewChild('renderFunctionExpansionPanel')
renderFunctionExpansionPanel: MatExpansionPanel;
@ViewChild('clickActionExpansionPanel')
clickActionExpansionPanel: MatExpansionPanel;
@ViewChild('stateRenderFunction')
stateRenderFunction: JsFuncComponent;
@ViewChild('clickAction')
clickAction: JsFuncComponent;
@Input() @Input()
disabled: boolean; disabled: boolean;
@ -131,6 +147,36 @@ export class ScadaSymbolMetadataTagComponent implements ControlValueAccessor, On
); );
} }
editTagStateRenderFunction(): void {
this.openPanelWithCallback(this.expansionPanel, () => {
this.openPanelWithCallback(this.renderFunctionExpansionPanel, () => {
this.stateRenderFunction.focus();
});
});
}
editClickAction(): void {
this.openPanelWithCallback(this.expansionPanel, () => {
this.openPanelWithCallback(this.clickActionExpansionPanel, () => {
this.clickAction.focus();
});
});
}
private openPanelWithCallback(panel: MatExpansionPanel, callback: () => void) {
if (!panel.expanded) {
const s = panel.afterExpand.subscribe(() => {
s.unsubscribe();
setTimeout(() => {
callback();
});
});
panel.open();
} else {
callback();
}
}
private updateModel() { private updateModel() {
const value = this.tagFormGroup.value; const value = this.tagFormGroup.value;
if (value.tag && !this.modelValue?.tag) { if (value.tag && !this.modelValue?.tag) {

3
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.scss

@ -14,7 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
.tb-scada-symbol-metadata-tags { .tb-scada-symbol-metadata-tags {
flex: 1;
margin: 12px;
.tb-scada-symbol-metadata-tags-list { .tb-scada-symbol-metadata-tags-list {
overflow: auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;

66
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component.ts

@ -14,7 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; import {
Component,
forwardRef, HostBinding,
Input,
OnChanges,
OnInit, QueryList,
SimpleChanges,
ViewChildren,
ViewEncapsulation
} from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -28,6 +37,9 @@ import {
Validator Validator
} from '@angular/forms'; } from '@angular/forms';
import { IotSvgTag } from '@home/components/widget/lib/svg/iot-svg.models'; import { IotSvgTag } from '@home/components/widget/lib/svg/iot-svg.models';
import {
ScadaSymbolMetadataTagComponent
} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tag.component';
const tagValidator = (control: AbstractControl): ValidationErrors | null => { const tagValidator = (control: AbstractControl): ValidationErrors | null => {
const tag: IotSvgTag = control.value; const tag: IotSvgTag = control.value;
@ -59,6 +71,12 @@ const tagValidator = (control: AbstractControl): ValidationErrors | null => {
}) })
export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, OnInit, Validator, OnChanges { export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, OnInit, Validator, OnChanges {
@HostBinding('style.display') styleDisplay = 'flex';
@HostBinding('style.overflow') styleOverflow = 'hidden';
@ViewChildren(ScadaSymbolMetadataTagComponent)
metadataTags: QueryList<ScadaSymbolMetadataTagComponent>;
@Input() @Input()
disabled: boolean; disabled: boolean;
@ -140,13 +158,44 @@ export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, O
return tagControl; return tagControl;
} }
removeTag(index: number) { removeTag(index: number, emitEvent = true) {
(this.tagsFormGroup.get('tags') as UntypedFormArray).removeAt(index); (this.tagsFormGroup.get('tags') as UntypedFormArray).removeAt(index, {emitEvent});
} }
addTag() { addTag() {
this.addNewTag(null);
}
editTagStateRenderFunction(tag: string): void {
this.addTagIfNotPresent(tag);
setTimeout(() => {
const tags: IotSvgTag[] = this.tagsFormGroup.get('tags').value;
const index = tags.findIndex(t => t.tag === tag);
const tagComponent = this.metadataTags.get(index);
tagComponent?.editTagStateRenderFunction();
});
}
editTagClickAction(tag: string): void {
this.addTagIfNotPresent(tag);
setTimeout(() => {
const tags: IotSvgTag[] = this.tagsFormGroup.get('tags').value;
const index = tags.findIndex(t => t.tag === tag);
const tagComponent = this.metadataTags.get(index);
tagComponent?.editClickAction();
});
}
private addTagIfNotPresent(tag: string) {
const tags: IotSvgTag[] = this.tagsFormGroup.get('tags').value;
if (!tags || !tags.find(t => t.tag === tag)) {
this.addNewTag(tag);
}
}
private addNewTag(tagLabel: string) {
const tag: IotSvgTag = { const tag: IotSvgTag = {
tag: null tag: tagLabel
}; };
const tagsArray = this.tagsFormGroup.get('tags') as UntypedFormArray; const tagsArray = this.tagsFormGroup.get('tags') as UntypedFormArray;
const tagControl = this.fb.control(tag, [tagValidator]); const tagControl = this.fb.control(tag, [tagValidator]);
@ -164,6 +213,15 @@ export class ScadaSymbolMetadataTagsComponent implements ControlValueAccessor, O
} }
private updateAvailableTags() { private updateAvailableTags() {
if (this.tags) {
for (const c of this.tagsFormArray().controls) {
const svgTag: IotSvgTag = c.value;
if (svgTag.tag && !this.tags.includes(svgTag.tag)) {
const index = this.tagsFormArray().controls.indexOf(c);
this.removeTag(index, false);
}
}
}
const svgTags: IotSvgTag[] = this.tagsFormGroup.get('tags').value; const svgTags: IotSvgTag[] = this.tagsFormGroup.get('tags').value;
const usedTags = (svgTags || []).filter(t => !!t.tag).map(t => t.tag); const usedTags = (svgTags || []).filter(t => !!t.tag).map(t => t.tag);
this.availableTags = (this.tags || []).filter(t => !usedTags.includes(t)); this.availableTags = (this.tags || []).filter(t => !usedTags.includes(t));

3
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.html

@ -51,8 +51,9 @@
</div> </div>
</div> </div>
</div> </div>
<div [fxShow]="selectedOption === 'tags'" class="mat-content"> <div [fxShow]="selectedOption === 'tags'" class="mat-content overflow-hidden">
<tb-scada-symbol-metadata-tags <tb-scada-symbol-metadata-tags
#symbolMetadataTags
[tags]="tags" [tags]="tags"
formControlName="tags"> formControlName="tags">
</tb-scada-symbol-metadata-tags> </tb-scada-symbol-metadata-tags>

4
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.scss

@ -41,6 +41,7 @@
} }
} }
.tb-scada-symbol-metadata-content { .tb-scada-symbol-metadata-content {
display: flex;
& > .mat-content { & > .mat-content {
padding-top: 8px; padding-top: 8px;
@media #{$mat-xs} { @media #{$mat-xs} {
@ -60,6 +61,9 @@
@media #{$mat-xs} { @media #{$mat-xs} {
gap: 8px; gap: 8px;
} }
&.overflow-hidden {
overflow: hidden;
}
} }
.tb-form-panel { .tb-form-panel {
.mat-expansion-panel.tb-settings { .mat-expansion-panel.tb-settings {

22
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, forwardRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALIDATORS, NG_VALIDATORS,
@ -29,9 +29,11 @@ import { PageComponent } from '@shared/components/page.component';
import { IotSvgMetadata } from '@home/components/widget/lib/svg/iot-svg.models'; import { IotSvgMetadata } from '@home/components/widget/lib/svg/iot-svg.models';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { WidgetConfigMode, widgetType } from '@shared/models/widget.models';
import { ToggleHeaderOption } from '@shared/components/toggle-header.component'; import { ToggleHeaderOption } from '@shared/components/toggle-header.component';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import {
ScadaSymbolMetadataTagsComponent
} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata-tags.component';
@Component({ @Component({
selector: 'tb-scada-symbol-metadata', selector: 'tb-scada-symbol-metadata',
@ -53,6 +55,9 @@ import { TranslateService } from '@ngx-translate/core';
}) })
export class ScadaSymbolMetadataComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator { export class ScadaSymbolMetadataComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator {
@ViewChild('symbolMetadataTags')
symbolMetadataTags: ScadaSymbolMetadataTagsComponent;
@Input() @Input()
disabled: boolean; disabled: boolean;
@ -130,6 +135,16 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
); );
} }
editTagStateRenderFunction(tag: string): void {
this.selectedOption = 'tags';
this.symbolMetadataTags.editTagStateRenderFunction(tag);
}
editTagClickAction(tag: string): void {
this.selectedOption = 'tags';
this.symbolMetadataTags.editTagClickAction(tag);
}
public validate(c: UntypedFormControl) { public validate(c: UntypedFormControl) {
const valid = this.metadataFormGroup.valid; const valid = this.metadataFormGroup.valid;
return valid ? null : { return valid ? null : {
@ -144,7 +159,4 @@ export class ScadaSymbolMetadataComponent extends PageComponent implements OnIni
this.modelValue = metadata; this.modelValue = metadata;
this.propagateChange(this.modelValue); this.propagateChange(this.modelValue);
} }
protected readonly WidgetConfigMode = WidgetConfigMode;
protected readonly widgetType = widgetType;
} }

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

@ -17,17 +17,17 @@
import { import {
AfterViewInit, AfterViewInit,
Component, Component,
ElementRef, EventEmitter, ElementRef,
Input, Input,
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit, Output, OnInit,
SimpleChanges, SimpleChanges,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { ScadaSymbolEditObject } from '@home/pages/scada-symbol/scada-symbol.models'; import { ScadaSymbolEditObject, ScadaSymbolEditObjectCallbacks } from '@home/pages/scada-symbol/scada-symbol.models';
export interface ScadaSymbolEditorData { export interface ScadaSymbolEditorData {
svgContent: string; svgContent: string;
@ -47,8 +47,8 @@ export class ScadaSymbolEditorComponent implements OnInit, OnDestroy, AfterViewI
@Input() @Input()
data: ScadaSymbolEditorData; data: ScadaSymbolEditorData;
@Output() @Input()
tags = new EventEmitter<string[]>(); editObjectCallbacks: ScadaSymbolEditObjectCallbacks;
scadaSymbolEditObject: ScadaSymbolEditObject; scadaSymbolEditObject: ScadaSymbolEditObject;
@ -60,8 +60,7 @@ export class ScadaSymbolEditorComponent implements OnInit, OnDestroy, AfterViewI
ngAfterViewInit() { ngAfterViewInit() {
this.scadaSymbolEditObject = new ScadaSymbolEditObject(this.iotSvgShape.nativeElement, this.scadaSymbolEditObject = new ScadaSymbolEditObject(this.iotSvgShape.nativeElement,
this.viewContainerRef); this.viewContainerRef, this.editObjectCallbacks);
this.scadaSymbolEditObject.tagsUpdated.subscribe(tags => this.tags.emit(tags));
if (this.data) { if (this.data) {
this.scadaSymbolEditObject.setContent(this.data.svgContent); this.scadaSymbolEditObject.setContent(this.data.svgContent);
} }
@ -86,13 +85,8 @@ export class ScadaSymbolEditorComponent implements OnInit, OnDestroy, AfterViewI
this.scadaSymbolEditObject.destroy(); this.scadaSymbolEditObject.destroy();
} }
get dirty(): boolean {
return this.scadaSymbolEditObject?.dirty;
}
getContent(): string { getContent(): string {
return this.scadaSymbolEditObject?.getContent(); return this.scadaSymbolEditObject?.getContent();
} }
} }

3
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-tooltip.component.scss

@ -21,6 +21,9 @@
&.column { &.column {
flex-direction: column; flex-direction: column;
} }
&.flex-start {
align-items: start;
}
.tb-confirm-text { .tb-confirm-text {
font-size: 14px; font-size: 14px;

119
ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-tooltip.components.ts

@ -189,6 +189,12 @@ class ScadaSymbolTagInputPanelComponent extends ScadaSymbolPanelComponent implem
(click)="onUpdateTag()"> (click)="onUpdateTag()">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button #tagSettingsButton type="button"
mat-icon-button class="tb-mat-20"
matTooltip="Settings"
matTooltipPosition="above">
<mat-icon>settings</mat-icon>
</button>
<button #removeTagButton type="button" <button #removeTagButton type="button"
mat-icon-button class="tb-mat-20" mat-icon-button class="tb-mat-20"
matTooltip="Remove tag" matTooltip="Remove tag"
@ -201,6 +207,9 @@ class ScadaSymbolTagInputPanelComponent extends ScadaSymbolPanelComponent implem
}) })
class ScadaSymbolTagPanelComponent extends ScadaSymbolPanelComponent implements AfterViewInit { class ScadaSymbolTagPanelComponent extends ScadaSymbolPanelComponent implements AfterViewInit {
@ViewChild('tagSettingsButton', {read: ElementRef})
tagSettingsButton: ElementRef<HTMLElement>;
@ViewChild('removeTagButton', {read: ElementRef}) @ViewChild('removeTagButton', {read: ElementRef})
removeTagButton: ElementRef<HTMLElement>; removeTagButton: ElementRef<HTMLElement>;
@ -218,8 +227,9 @@ class ScadaSymbolTagPanelComponent extends ScadaSymbolPanelComponent implements
ngAfterViewInit() { ngAfterViewInit() {
super.ngAfterViewInit(); super.ngAfterViewInit();
setTimeout(() => { setTimeout(() => {
const el = $(this.removeTagButton.nativeElement);
el.tooltipster( const tagSettingsButton = $(this.tagSettingsButton.nativeElement);
tagSettingsButton.tooltipster(
{ {
zIndex: 200, zIndex: 200,
arrow: true, arrow: true,
@ -230,18 +240,39 @@ class ScadaSymbolTagPanelComponent extends ScadaSymbolPanelComponent implements
content: '' content: ''
} }
); );
const tooltip = el.tooltipster('instance');
const compRef = const scadaSymbolTagSettingsTooltip = tagSettingsButton.tooltipster('instance');
setTooltipComponent(this.symbolElement, this.container, ScadaSymbolRemoveTagConfirmComponent, tooltip); const scadaSymbolTagSettingsCompRef =
compRef.instance.removeTag.subscribe(() => { setTooltipComponent(this.symbolElement, this.container, ScadaSymbolTagSettingsComponent, scadaSymbolTagSettingsTooltip);
tooltip.destroy(); scadaSymbolTagSettingsTooltip.on('ready', () => {
scadaSymbolTagSettingsCompRef.instance.updateFunctionsState();
});
const removeTagButton = $(this.removeTagButton.nativeElement);
removeTagButton.tooltipster(
{
zIndex: 200,
arrow: true,
theme: ['iot-svg', 'tb-active'],
interactive: true,
trigger: 'click',
side: 'top',
content: ''
}
);
const scadaSymbolRemoveTagTooltip = removeTagButton.tooltipster('instance');
const scadaSymbolRemoveTagCompRef =
setTooltipComponent(this.symbolElement, this.container, ScadaSymbolRemoveTagConfirmComponent, scadaSymbolRemoveTagTooltip);
scadaSymbolRemoveTagCompRef.instance.removeTag.subscribe(() => {
scadaSymbolRemoveTagTooltip.destroy();
this.removeTag.emit(); this.removeTag.emit();
}); });
compRef.instance.cancel.subscribe(() => { scadaSymbolRemoveTagCompRef.instance.cancel.subscribe(() => {
tooltip.close(); scadaSymbolRemoveTagTooltip.close();
}); });
tooltip.on('ready', () => { scadaSymbolRemoveTagTooltip.on('ready', () => {
compRef.instance.yesButton.focus(); scadaSymbolRemoveTagCompRef.instance.yesButton.focus();
}); });
}); });
} }
@ -300,13 +331,77 @@ class ScadaSymbolRemoveTagConfirmComponent extends ScadaSymbolPanelComponent imp
} }
} }
@Component({
template: `<div class="tooltipster-content tb-scada-symbol-tooltip-panel column flex-start">
<div translate>scada.state-render-function</div>
<button *ngIf="hasStateRenderFunction"
mat-stroked-button
color="primary"
(click)="editStateRenderFunction()">
<mat-icon>edit</mat-icon>
<span>Edit</span>
</button>
<button *ngIf="!hasStateRenderFunction"
mat-stroked-button
color="primary"
(click)="editStateRenderFunction()">
<mat-icon>add</mat-icon>
<span>Add</span>
</button>
<div translate>scada.on-click-action</div>
<button *ngIf="hasClickAction"
mat-stroked-button
color="primary"
(click)="editClickAction()">
<mat-icon>edit</mat-icon>
<span>Edit</span>
</button>
<button *ngIf="!hasClickAction"
mat-stroked-button
color="primary"
(click)="editClickAction()">
<mat-icon>add</mat-icon>
<span>Add</span>
</button>
</div>`,
styleUrls: ['./scada-symbol-tooltip.component.scss'],
encapsulation: ViewEncapsulation.None
})
class ScadaSymbolTagSettingsComponent extends ScadaSymbolPanelComponent implements OnInit, AfterViewInit {
hasStateRenderFunction = false;
hasClickAction = false;
constructor(public element: ElementRef<HTMLElement>) {
super(element);
}
ngOnInit() {
}
updateFunctionsState() {
this.hasStateRenderFunction = this.symbolElement.hasStateRenderFunction();
this.hasClickAction = this.symbolElement.hasClickAction();
}
editStateRenderFunction() {
this.symbolElement.editStateRenderFunction();
}
editClickAction() {
this.symbolElement.editClickAction();
}
}
@NgModule({ @NgModule({
declarations: declarations:
[ [
ScadaSymbolAddTagPanelComponent, ScadaSymbolAddTagPanelComponent,
ScadaSymbolTagInputPanelComponent, ScadaSymbolTagInputPanelComponent,
ScadaSymbolTagPanelComponent, ScadaSymbolTagPanelComponent,
ScadaSymbolRemoveTagConfirmComponent ScadaSymbolRemoveTagConfirmComponent,
ScadaSymbolTagSettingsComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

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

@ -64,6 +64,7 @@
</div> </div>
<div [fxShow]="!previewMode" [formGroup]="scadaSymbolFormGroup"> <div [fxShow]="!previewMode" [formGroup]="scadaSymbolFormGroup">
<tb-scada-symbol-metadata <tb-scada-symbol-metadata
#symbolMetadata
[tags]="tags" [tags]="tags"
formControlName="metadata"> formControlName="metadata">
<div class="tb-scada-symbol-metadata-header-prefix"> <div class="tb-scada-symbol-metadata-header-prefix">
@ -84,13 +85,13 @@
{{ 'scada.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 || symbolEditorDirty)"
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 || symbolEditorDirty)"
mat-flat-button color="primary" mat-flat-button color="primary"
(click)="onApplyScadaSymbolConfig()"> (click)="onApplyScadaSymbolConfig()">
<mat-icon>done</mat-icon> <mat-icon>done</mat-icon>
@ -105,7 +106,7 @@
<mat-drawer-content class="tb-scada-symbol-content"> <mat-drawer-content class="tb-scada-symbol-content">
<tb-scada-symbol-editor *ngIf="!previewMode" #symbolEditor <tb-scada-symbol-editor *ngIf="!previewMode" #symbolEditor
[data]="symbolEditorData" [data]="symbolEditorData"
(tags)="tags = $event"></tb-scada-symbol-editor> [editObjectCallbacks]="editObjectCallbacks"></tb-scada-symbol-editor>
<tb-dashboard *ngIf="previewMode" <tb-dashboard *ngIf="previewMode"
class="tb-absolute-fill" class="tb-absolute-fill"
[aliasController]="aliasController" [aliasController]="aliasController"

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

@ -31,7 +31,7 @@ import { AppState } from '@core/core.state';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { map, switchMap, 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, ScadaSymbolEditObjectCallbacks } from '@home/pages/scada-symbol/scada-symbol.models';
import { import {
IotSvgMetadata, IotSvgObjectSettings, IotSvgMetadata, IotSvgObjectSettings,
parseIotSvgMetadataFromContent, parseIotSvgMetadataFromContent,
@ -59,6 +59,9 @@ import {
IotSvgWidgetSettings IotSvgWidgetSettings
} from '@home/components/widget/lib/svg/iot-svg-widget.models'; } from '@home/components/widget/lib/svg/iot-svg-widget.models';
import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models';
import {
ScadaSymbolMetadataComponent
} from '@home/pages/scada-symbol/metadata-components/scada-symbol-metadata.component';
@Component({ @Component({
selector: 'tb-scada-symbol', selector: 'tb-scada-symbol',
@ -66,7 +69,8 @@ import { WidgetActionCallbacks } from '@home/components/widget/action/manage-wid
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, HasDirtyFlag { export class ScadaSymbolComponent extends PageComponent
implements OnInit, OnDestroy, AfterViewInit, HasDirtyFlag, ScadaSymbolEditObjectCallbacks {
widgetType = widgetType; widgetType = widgetType;
@ -76,6 +80,9 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
@ViewChild('symbolEditor', {static: false}) @ViewChild('symbolEditor', {static: false})
symbolEditor: ScadaSymbolEditorComponent; symbolEditor: ScadaSymbolEditorComponent;
@ViewChild('symbolMetadata')
symbolMetadata: ScadaSymbolMetadataComponent;
symbolData: ScadaSymbolData; symbolData: ScadaSymbolData;
symbolEditorData: ScadaSymbolEditorData; symbolEditorData: ScadaSymbolEditorData;
@ -102,6 +109,10 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
tags: string[]; tags: string[];
editObjectCallbacks: ScadaSymbolEditObjectCallbacks = this;
symbolEditorDirty = false;
private previewIotSvgObjectSettings: IotSvgObjectSettings; private previewIotSvgObjectSettings: IotSvgObjectSettings;
private previewWidget: Widget; private previewWidget: Widget;
@ -109,7 +120,7 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
private forcePristine = false; private forcePristine = false;
get isDirty(): boolean { get isDirty(): boolean {
return (this.scadaSymbolFormGroup.dirty || this.symbolEditor?.dirty) && !this.forcePristine; return (this.scadaSymbolFormGroup.dirty || this.symbolEditorDirty) && !this.forcePristine;
} }
set isDirty(value: boolean) { set isDirty(value: boolean) {
@ -231,9 +242,6 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
}; };
this.previewWidgets = [this.previewWidget]; this.previewWidgets = [this.previewWidget];
this.previewMode = true; this.previewMode = true;
/*setTimeout(() => {
this.updatePreviewWidgetSettings();
}, 2000);*/
} }
exitPreviewMode() { exitPreviewMode() {
@ -256,6 +264,40 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
this.updatePreviewWidgetSettings(); this.updatePreviewWidgetSettings();
} }
tagsUpdated(tags: string[]) {
this.tags = tags;
}
tagHasStateRenderFunction(tag: string): boolean {
const metadata: IotSvgMetadata = this.scadaSymbolFormGroup.get('metadata').value;
if (metadata.tags) {
const found = metadata.tags.find(t => t.tag === tag);
return !!found?.stateRenderFunction;
}
return false;
}
tagHasClickAction(tag: string): boolean {
const metadata: IotSvgMetadata = this.scadaSymbolFormGroup.get('metadata').value;
if (metadata.tags) {
const found = metadata.tags.find(t => t.tag === tag);
return !!found?.actions?.click?.actionFunction;
}
return false;
}
editTagStateRenderFunction(tag: string) {
this.symbolMetadata.editTagStateRenderFunction(tag);
}
editTagClickAction(tag: string) {
this.symbolMetadata.editTagClickAction(tag);
}
onSymbolEditObjectDirty(dirty: boolean) {
this.symbolEditorDirty = dirty;
}
private updatePreviewWidgetSettings() { private updatePreviewWidgetSettings() {
this.previewWidget = deepClone(this.previewWidget); this.previewWidget = deepClone(this.previewWidget);
this.previewWidget.config.settings.iotSvgObject = this.previewIotSvgObjectSettings; this.previewWidget.config.settings.iotSvgObject = this.previewIotSvgObjectSettings;
@ -283,6 +325,7 @@ export class ScadaSymbolComponent extends PageComponent implements OnInit, OnDes
metadata metadata
}, {emitEvent: false}); }, {emitEvent: false});
this.scadaSymbolFormGroup.markAsPristine(); this.scadaSymbolFormGroup.markAsPristine();
this.symbolEditorDirty = false;
this.cd.markForCheck(); this.cd.markForCheck();
} }
} }

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

@ -18,7 +18,7 @@ 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 { EventEmitter, ViewContainerRef } from '@angular/core'; import { ViewContainerRef } from '@angular/core';
import { forkJoin, from } from 'rxjs'; import { forkJoin, from } from 'rxjs';
import { import {
setupAddTagPanelTooltip, setupAddTagPanelTooltip,
@ -35,6 +35,15 @@ export interface ScadaSymbolData {
svgContent: string; svgContent: string;
} }
export interface ScadaSymbolEditObjectCallbacks {
tagHasStateRenderFunction: (tag: string) => boolean;
tagHasClickAction: (tag: string) => boolean;
editTagStateRenderFunction: (tag: string) => void;
editTagClickAction: (tag: string) => void;
tagsUpdated: (tags: string[]) => void;
onSymbolEditObjectDirty: (dirty: boolean) => void;
}
export class ScadaSymbolEditObject { export class ScadaSymbolEditObject {
public svgShape: Svg; public svgShape: Svg;
@ -46,14 +55,11 @@ export class ScadaSymbolEditObject {
private hoverFilterStyle: Style; private hoverFilterStyle: Style;
public scale = 1; public scale = 1;
public dirty = false;
public tags: string[] = []; public tags: string[] = [];
public tagsUpdated = new EventEmitter<string[]>();
constructor(private rootElement: HTMLElement, constructor(private rootElement: HTMLElement,
public viewContainerRef: ViewContainerRef) { public viewContainerRef: ViewContainerRef,
private callbacks: ScadaSymbolEditObjectCallbacks) {
this.shapeResize$ = new ResizeObserver(() => { this.shapeResize$ = new ResizeObserver(() => {
this.resize(); this.resize();
}); });
@ -66,7 +72,6 @@ export class ScadaSymbolEditObject {
this.svgShape.remove(); this.svgShape.remove();
} }
this.scale = 1; this.scale = 1;
this.dirty = false;
const contentData = iotSvgContentData(svgContent); const contentData = iotSvgContentData(svgContent);
this.svgRootNodePart = contentData.svgRootNode; this.svgRootNodePart = contentData.svgRootNode;
this.svgShape = SVG().addTo(this.rootElement).svg(contentData.innerSvg); this.svgShape = SVG().addTo(this.rootElement).svg(contentData.innerSvg);
@ -227,7 +232,6 @@ export class ScadaSymbolEditObject {
this.shapeResize$.disconnect(); this.shapeResize$.disconnect();
} }
this.destroyElements(); this.destroyElements();
this.tagsUpdated.complete();
} }
private destroyElements() { private destroyElements() {
@ -299,7 +303,27 @@ export class ScadaSymbolEditObject {
.map(e => e.tag) .map(e => e.tag)
.filter((v, i, a) => a.indexOf(v) === i) .filter((v, i, a) => a.indexOf(v) === i)
.sort(); .sort();
this.tagsUpdated.emit(this.tags); this.callbacks.tagsUpdated(this.tags);
}
public tagHasStateRenderFunction(tag: string): boolean {
return this.callbacks.tagHasStateRenderFunction(tag);
}
public tagHasClickAction(tag: string): boolean {
return this.callbacks.tagHasClickAction(tag);
}
public editTagStateRenderFunction(tag: string) {
this.callbacks.editTagStateRenderFunction(tag);
}
public editTagClickAction(tag: string) {
this.callbacks.editTagClickAction(tag);
}
public setDirty(dirty: boolean) {
this.callbacks.onSymbolEditObjectDirty(dirty);
} }
} }
@ -444,7 +468,7 @@ export class ScadaSymbolElement {
this.element.attr('tb:tag', null); this.element.attr('tb:tag', null);
this.unhighlight(); this.unhighlight();
this.createAddTagTooltip(); this.createAddTagTooltip();
this.editObject.dirty = true; this.editObject.setDirty(true);
this.editObject.updateTags(); this.editObject.updateTags();
} }
@ -453,10 +477,38 @@ export class ScadaSymbolElement {
this.tag = tag; this.tag = tag;
this.element.attr('tb:tag', tag); this.element.attr('tb:tag', tag);
this.createTagTooltip(); this.createTagTooltip();
this.editObject.dirty = true; this.editObject.setDirty(true);
this.editObject.updateTags(); this.editObject.updateTags();
} }
public hasStateRenderFunction(): boolean {
if (this.hasTag()) {
return this.editObject.tagHasStateRenderFunction(this.tag);
} else {
return false;
}
}
public hasClickAction(): boolean {
if (this.hasTag()) {
return this.editObject.tagHasClickAction(this.tag);
} else {
return false;
}
}
public editStateRenderFunction() {
if (this.hasTag()) {
this.editObject.editTagStateRenderFunction(this.tag);
}
}
public editClickAction() {
if (this.hasTag()) {
this.editObject.editTagClickAction(this.tag);
}
}
public startEdit(onCancelEdit: () => void) { public startEdit(onCancelEdit: () => void) {
if (!this.editing) { if (!this.editing) {
this.editObject.cancelEdit(); this.editObject.cancelEdit();

5
ui-ngx/src/app/shared/components/js-func.component.ts

@ -317,6 +317,11 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor,
} }
} }
public focus() {
this.javascriptEditorElmRef.nativeElement.scrollIntoView();
this.jsEditor?.focus();
}
private validateJsFunc(): boolean { private validateJsFunc(): boolean {
try { try {
const toValidate = new Function(this.functionArgsString, this.modelValue); const toValidate = new Function(this.functionArgsString, this.modelValue);

Loading…
Cancel
Save