500 lines
14 KiB
TypeScript
500 lines
14 KiB
TypeScript
///
|
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
|
|
import {
|
|
AfterViewInit,
|
|
Component,
|
|
ComponentRef,
|
|
Directive,
|
|
ElementRef,
|
|
EventEmitter,
|
|
Input,
|
|
NgModule, OnDestroy,
|
|
OnInit,
|
|
Output,
|
|
Type,
|
|
ViewChild,
|
|
ViewContainerRef,
|
|
ViewEncapsulation
|
|
} from '@angular/core';
|
|
import { ScadaSymbolElement } from '@home/pages/scada-symbol/scada-symbol.models';
|
|
import { CommonModule } from '@angular/common';
|
|
import { SharedModule } from '@shared/shared.module';
|
|
import { ENTER } from '@angular/cdk/keycodes';
|
|
import Timeout = NodeJS.Timeout;
|
|
import { MatButton } from '@angular/material/button';
|
|
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
|
|
|
|
@Directive()
|
|
abstract class ScadaSymbolPanelComponent implements AfterViewInit {
|
|
|
|
@Input()
|
|
symbolElement: ScadaSymbolElement;
|
|
|
|
@Output()
|
|
viewInited = new EventEmitter();
|
|
|
|
protected constructor(public element: ElementRef<HTMLElement>) {
|
|
}
|
|
|
|
ngAfterViewInit() {
|
|
this.viewInited.emit();
|
|
}
|
|
}
|
|
|
|
@Component({
|
|
template: `<div class="tb-scada-symbol-tooltip-panel">
|
|
<span>{{ symbolElement?.element?.type }}:</span>
|
|
<button mat-stroked-button color="primary" (click)="onAddTag()">
|
|
<mat-icon>add</mat-icon>
|
|
<span>Add tag</span>
|
|
</button>
|
|
</div>`,
|
|
styleUrls: ['./scada-symbol-tooltip.component.scss'],
|
|
encapsulation: ViewEncapsulation.None
|
|
})
|
|
class ScadaSymbolAddTagPanelComponent extends ScadaSymbolPanelComponent {
|
|
|
|
@Output()
|
|
addTag = new EventEmitter();
|
|
|
|
constructor(public element: ElementRef<HTMLElement>) {
|
|
super(element);
|
|
}
|
|
|
|
public onAddTag() {
|
|
this.addTag.emit();
|
|
}
|
|
|
|
}
|
|
|
|
@Component({
|
|
template: `<div class="tb-scada-symbol-tooltip-panel">
|
|
<span>{{ isAdd ? 'Enter tag:' : 'Update tag:' }}</span>
|
|
<mat-form-field class="tb-inline-field" appearance="outline" subscriptSizing="dynamic">
|
|
<input #tagField matInput [(ngModel)]="tag" (keydown)="tagEnter($event)" (blur)="onBlur()"
|
|
placeholder="{{ 'widget-config.set' | translate }}">
|
|
</mat-form-field>
|
|
<button type="button" mat-icon-button class="tb-mat-20"
|
|
matTooltip="Apply"
|
|
matTooltipPosition="above"
|
|
[disabled]="!tag"
|
|
(click)="onApply()">
|
|
<mat-icon>done</mat-icon>
|
|
</button>
|
|
<button type="button" mat-icon-button class="tb-mat-20"
|
|
matTooltip="Cancel"
|
|
matTooltipPosition="above"
|
|
(click)="onCancel()">
|
|
<mat-icon>close</mat-icon>
|
|
</button>
|
|
</div>`,
|
|
styleUrls: ['./scada-symbol-tooltip.component.scss'],
|
|
encapsulation: ViewEncapsulation.None
|
|
})
|
|
class ScadaSymbolTagInputPanelComponent extends ScadaSymbolPanelComponent implements AfterViewInit, OnDestroy {
|
|
|
|
@ViewChild('tagField')
|
|
tagField: ElementRef<HTMLInputElement>;
|
|
|
|
@Input()
|
|
isAdd: boolean;
|
|
|
|
@Input()
|
|
tag: string;
|
|
|
|
@Output()
|
|
apply = new EventEmitter<string>();
|
|
|
|
@Output()
|
|
cancel = new EventEmitter();
|
|
|
|
private closed = false;
|
|
|
|
private blurTimeout: Timeout;
|
|
|
|
constructor(public element: ElementRef<HTMLElement>) {
|
|
super(element);
|
|
}
|
|
|
|
ngAfterViewInit() {
|
|
super.ngAfterViewInit();
|
|
setTimeout(() => {
|
|
this.tagField.nativeElement.focus();
|
|
});
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
if (this.blurTimeout) {
|
|
clearTimeout(this.blurTimeout);
|
|
this.blurTimeout = null;
|
|
}
|
|
}
|
|
|
|
public tagEnter($event: KeyboardEvent) {
|
|
if ($event.keyCode === ENTER) {
|
|
$event.preventDefault();
|
|
if (this.tag) {
|
|
this.closed = true;
|
|
this.apply.emit(this.tag);
|
|
}
|
|
}
|
|
}
|
|
|
|
public onApply() {
|
|
this.closed = true;
|
|
if (this.tag) {
|
|
this.apply.emit(this.tag);
|
|
} else {
|
|
this.cancel.emit();
|
|
}
|
|
}
|
|
|
|
public onCancel() {
|
|
this.closed = true;
|
|
this.cancel.emit();
|
|
}
|
|
|
|
public onBlur() {
|
|
this.blurTimeout = setTimeout(() => {
|
|
if (!this.closed) {
|
|
this.closed = true;
|
|
this.cancel.emit();
|
|
}
|
|
}, 300);
|
|
}
|
|
|
|
}
|
|
|
|
@Component({
|
|
template: `<div class="tb-scada-symbol-tooltip-panel">
|
|
<span>{{ symbolElement?.element?.type }}:</span>
|
|
<span><b>{{ symbolElement?.tag }}</b></span>
|
|
<button type="button" mat-icon-button class="tb-mat-20"
|
|
matTooltip="Update tag"
|
|
matTooltipPosition="above"
|
|
(click)="onUpdateTag()">
|
|
<mat-icon>edit</mat-icon>
|
|
</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"
|
|
mat-icon-button class="tb-mat-20"
|
|
matTooltip="Remove tag"
|
|
matTooltipPosition="above">
|
|
<mat-icon>delete</mat-icon>
|
|
</button>
|
|
</div>`,
|
|
styleUrls: ['./scada-symbol-tooltip.component.scss'],
|
|
encapsulation: ViewEncapsulation.None
|
|
})
|
|
class ScadaSymbolTagPanelComponent extends ScadaSymbolPanelComponent implements AfterViewInit {
|
|
|
|
@ViewChild('tagSettingsButton', {read: ElementRef})
|
|
tagSettingsButton: ElementRef<HTMLElement>;
|
|
|
|
@ViewChild('removeTagButton', {read: ElementRef})
|
|
removeTagButton: ElementRef<HTMLElement>;
|
|
|
|
@Output()
|
|
updateTag = new EventEmitter();
|
|
|
|
@Output()
|
|
removeTag = new EventEmitter();
|
|
|
|
constructor(public element: ElementRef<HTMLElement>,
|
|
private container: ViewContainerRef) {
|
|
super(element);
|
|
}
|
|
|
|
ngAfterViewInit() {
|
|
super.ngAfterViewInit();
|
|
setTimeout(() => {
|
|
|
|
const tagSettingsButton = $(this.tagSettingsButton.nativeElement);
|
|
tagSettingsButton.tooltipster(
|
|
{
|
|
zIndex: 200,
|
|
arrow: true,
|
|
theme: ['iot-svg', 'tb-active'],
|
|
interactive: true,
|
|
trigger: 'click',
|
|
side: 'top',
|
|
content: ''
|
|
}
|
|
);
|
|
|
|
const scadaSymbolTagSettingsTooltip = tagSettingsButton.tooltipster('instance');
|
|
const scadaSymbolTagSettingsCompRef =
|
|
setTooltipComponent(this.symbolElement, this.container, ScadaSymbolTagSettingsComponent, scadaSymbolTagSettingsTooltip);
|
|
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();
|
|
});
|
|
scadaSymbolRemoveTagCompRef.instance.cancel.subscribe(() => {
|
|
scadaSymbolRemoveTagTooltip.close();
|
|
});
|
|
scadaSymbolRemoveTagTooltip.on('ready', () => {
|
|
scadaSymbolRemoveTagCompRef.instance.yesButton.focus();
|
|
});
|
|
});
|
|
}
|
|
|
|
public onUpdateTag() {
|
|
this.updateTag.emit();
|
|
}
|
|
}
|
|
|
|
@Component({
|
|
template: `<div class="tooltipster-content tb-scada-symbol-tooltip-panel column">
|
|
<div class="tb-confirm-text" [innerHTML]="deleteText"></div>
|
|
<div class="tb-scada-symbol-tooltip-panel">
|
|
<button mat-stroked-button color="primary" (click)="onCancel()">
|
|
<mat-icon>close</mat-icon>
|
|
<span>No</span>
|
|
</button>
|
|
<button #yesButton
|
|
mat-stroked-button color="primary" (click)="onRemoveTag()">
|
|
<mat-icon>done</mat-icon>
|
|
<span>Yes</span>
|
|
</button>
|
|
</div>
|
|
</div>`,
|
|
styleUrls: ['./scada-symbol-tooltip.component.scss'],
|
|
encapsulation: ViewEncapsulation.None
|
|
})
|
|
class ScadaSymbolRemoveTagConfirmComponent extends ScadaSymbolPanelComponent implements OnInit, AfterViewInit {
|
|
|
|
@ViewChild('yesButton')
|
|
yesButton: MatButton;
|
|
|
|
deleteText: string;
|
|
|
|
@Output()
|
|
cancel = new EventEmitter();
|
|
|
|
@Output()
|
|
removeTag = new EventEmitter();
|
|
|
|
constructor(public element: ElementRef<HTMLElement>) {
|
|
super(element);
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.deleteText = `Are you sure you want to delete tag<br/><b>${this.symbolElement?.tag}</b>
|
|
from <b>${this.symbolElement?.element?.type}</b> element?`;
|
|
}
|
|
|
|
public onCancel() {
|
|
this.cancel.emit();
|
|
}
|
|
|
|
public onRemoveTag() {
|
|
this.removeTag.emit();
|
|
}
|
|
}
|
|
|
|
@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({
|
|
declarations:
|
|
[
|
|
ScadaSymbolAddTagPanelComponent,
|
|
ScadaSymbolTagInputPanelComponent,
|
|
ScadaSymbolTagPanelComponent,
|
|
ScadaSymbolRemoveTagConfirmComponent,
|
|
ScadaSymbolTagSettingsComponent
|
|
],
|
|
imports: [
|
|
CommonModule,
|
|
SharedModule
|
|
]
|
|
})
|
|
export class ScadaSymbolTooltipComponentsModule { }
|
|
|
|
export const setupAddTagPanelTooltip = (symbolElement: ScadaSymbolElement, container: ViewContainerRef) => {
|
|
symbolElement.stopEdit();
|
|
symbolElement.tooltip.off('close');
|
|
const componentRef = setTooltipComponent(symbolElement, container, ScadaSymbolAddTagPanelComponent);
|
|
componentRef.instance.addTag.subscribe(() => {
|
|
componentRef.destroy();
|
|
setupTagInputPanelTooltip(symbolElement, container, true);
|
|
});
|
|
};
|
|
|
|
export const setupTagPanelTooltip = (symbolElement: ScadaSymbolElement, container: ViewContainerRef) => {
|
|
symbolElement.stopEdit();
|
|
symbolElement.unhighlight();
|
|
const componentRef = setTooltipComponent(symbolElement, container, ScadaSymbolTagPanelComponent);
|
|
componentRef.instance.updateTag.subscribe(() => {
|
|
componentRef.destroy();
|
|
setupTagInputPanelTooltip(symbolElement, container, false);
|
|
});
|
|
componentRef.instance.removeTag.subscribe(() => {
|
|
componentRef.destroy();
|
|
symbolElement.clearTag();
|
|
});
|
|
symbolElement.tooltip.open();
|
|
};
|
|
|
|
const setupTagInputPanelTooltip = (symbolElement: ScadaSymbolElement, container: ViewContainerRef, isAdd: boolean) => {
|
|
|
|
symbolElement.startEdit(() => {
|
|
if (isAdd) {
|
|
symbolElement.unhighlight();
|
|
symbolElement.tooltip.close();
|
|
} else {
|
|
componentRef.destroy();
|
|
setupTagPanelTooltip(symbolElement, container);
|
|
}
|
|
});
|
|
|
|
const componentRef = setTooltipComponent(symbolElement, container, ScadaSymbolTagInputPanelComponent);
|
|
|
|
componentRef.instance.isAdd = isAdd;
|
|
if (!isAdd) {
|
|
componentRef.instance.tag = symbolElement.tag;
|
|
}
|
|
componentRef.instance.apply.subscribe((newTag) => {
|
|
componentRef.destroy();
|
|
if (isAdd) {
|
|
symbolElement.tooltip.off('close');
|
|
}
|
|
symbolElement.setTag(newTag);
|
|
});
|
|
componentRef.instance.cancel.subscribe(() => {
|
|
symbolElement.stopEdit(true);
|
|
});
|
|
if (isAdd) {
|
|
symbolElement.tooltip.option('delay', [0, 10000000]);
|
|
symbolElement.tooltip.on('close', () => {
|
|
componentRef.destroy();
|
|
symbolElement.tooltip.option('delay', [0, 300]);
|
|
setupAddTagPanelTooltip(symbolElement, container);
|
|
});
|
|
}
|
|
};
|
|
|
|
const setTooltipComponent = <T extends ScadaSymbolPanelComponent>(symbolElement: ScadaSymbolElement,
|
|
container: ViewContainerRef,
|
|
componentType: Type<T>,
|
|
tooltip?: ITooltipsterInstance): ComponentRef<T> => {
|
|
if (!tooltip) {
|
|
tooltip = symbolElement.tooltip;
|
|
}
|
|
const componentRef = container.createComponent(componentType);
|
|
componentRef.instance.symbolElement = symbolElement;
|
|
componentRef.instance.viewInited.subscribe(() => {
|
|
if (tooltip.status().open) {
|
|
tooltip.reposition();
|
|
}
|
|
});
|
|
tooltip.on('destroyed', () => {
|
|
componentRef.destroy();
|
|
});
|
|
const parentElement = componentRef.instance.element.nativeElement;
|
|
const content = parentElement.firstChild;
|
|
parentElement.removeChild(content);
|
|
parentElement.style.display = 'none';
|
|
tooltip.content(content);
|
|
return componentRef;
|
|
};
|