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

405 lines
11 KiB
TypeScript
Raw Normal View History

2024-05-17 19:31:35 +03:00
///
/// 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,
2024-05-17 20:25:51 +03:00
NgModule, OnDestroy,
2024-05-20 18:08:25 +03:00
OnInit,
2024-05-17 19:31:35 +03:00
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';
2024-05-17 20:25:51 +03:00
import Timeout = NodeJS.Timeout;
2024-05-20 18:08:25 +03:00
import { MatButton } from '@angular/material/button';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
2024-05-17 19:31:35 +03:00
@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>
2024-05-20 18:08:25 +03:00
<button mat-stroked-button color="primary" (click)="onAddTag()">
<mat-icon>add</mat-icon>
<span>Add tag</span>
</button>
2024-05-17 19:31:35 +03:00
</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()">
2024-05-20 18:08:25 +03:00
<mat-icon>done</mat-icon>
2024-05-17 19:31:35 +03:00
</button>
<button type="button" mat-icon-button class="tb-mat-20"
matTooltip="Cancel"
matTooltipPosition="above"
(click)="onCancel()">
2024-05-20 18:08:25 +03:00
<mat-icon>close</mat-icon>
2024-05-17 19:31:35 +03:00
</button>
</div>`,
styleUrls: ['./scada-symbol-tooltip.component.scss'],
encapsulation: ViewEncapsulation.None
})
2024-05-17 20:25:51 +03:00
class ScadaSymbolTagInputPanelComponent extends ScadaSymbolPanelComponent implements AfterViewInit, OnDestroy {
2024-05-17 19:31:35 +03:00
@ViewChild('tagField')
tagField: ElementRef<HTMLInputElement>;
@Input()
isAdd: boolean;
@Input()
tag: string;
@Output()
apply = new EventEmitter<string>();
@Output()
cancel = new EventEmitter();
private closed = false;
2024-05-17 20:25:51 +03:00
private blurTimeout: Timeout;
2024-05-17 19:31:35 +03:00
constructor(public element: ElementRef<HTMLElement>) {
super(element);
}
ngAfterViewInit() {
super.ngAfterViewInit();
setTimeout(() => {
this.tagField.nativeElement.focus();
});
}
2024-05-17 20:25:51 +03:00
ngOnDestroy() {
if (this.blurTimeout) {
clearTimeout(this.blurTimeout);
this.blurTimeout = null;
}
}
2024-05-17 19:31:35 +03:00
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() {
2024-05-17 20:25:51 +03:00
this.blurTimeout = setTimeout(() => {
2024-05-17 19:31:35 +03:00
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()">
2024-05-20 18:08:25 +03:00
<mat-icon>edit</mat-icon>
2024-05-17 19:31:35 +03:00
</button>
2024-05-20 18:08:25 +03:00
<button #removeTagButton type="button"
mat-icon-button class="tb-mat-20"
2024-05-17 19:31:35 +03:00
matTooltip="Remove tag"
2024-05-20 18:08:25 +03:00
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
2024-05-17 19:31:35 +03:00
</button>
</div>`,
styleUrls: ['./scada-symbol-tooltip.component.scss'],
encapsulation: ViewEncapsulation.None
})
class ScadaSymbolTagPanelComponent extends ScadaSymbolPanelComponent implements AfterViewInit {
2024-05-20 18:08:25 +03:00
@ViewChild('removeTagButton', {read: ElementRef})
removeTagButton: ElementRef<HTMLElement>;
2024-05-17 19:31:35 +03:00
@Output()
updateTag = new EventEmitter();
@Output()
removeTag = new EventEmitter();
2024-05-20 18:08:25 +03:00
constructor(public element: ElementRef<HTMLElement>,
private container: ViewContainerRef) {
2024-05-17 19:31:35 +03:00
super(element);
}
2024-05-20 18:08:25 +03:00
ngAfterViewInit() {
super.ngAfterViewInit();
setTimeout(() => {
const el = $(this.removeTagButton.nativeElement);
el.tooltipster(
{
zIndex: 200,
arrow: true,
theme: ['iot-svg', 'tb-active'],
interactive: true,
trigger: 'click',
side: 'top',
content: ''
}
);
const tooltip = el.tooltipster('instance');
const compRef =
setTooltipComponent(this.symbolElement, this.container, ScadaSymbolRemoveTagConfirmComponent, tooltip);
compRef.instance.removeTag.subscribe(() => {
tooltip.destroy();
this.removeTag.emit();
});
compRef.instance.cancel.subscribe(() => {
tooltip.close();
});
tooltip.on('ready', () => {
compRef.instance.yesButton.focus();
});
});
}
2024-05-17 19:31:35 +03:00
public onUpdateTag() {
this.updateTag.emit();
}
2024-05-20 18:08:25 +03:00
}
@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();
}
2024-05-17 19:31:35 +03:00
public onRemoveTag() {
this.removeTag.emit();
}
}
@NgModule({
declarations:
[
ScadaSymbolAddTagPanelComponent,
ScadaSymbolTagInputPanelComponent,
2024-05-20 18:08:25 +03:00
ScadaSymbolTagPanelComponent,
ScadaSymbolRemoveTagConfirmComponent
2024-05-17 19:31:35 +03:00
],
imports: [
CommonModule,
SharedModule
]
})
export class ScadaSymbolTooltipComponentsModule { }
export const setupAddTagPanelTooltip = (symbolElement: ScadaSymbolElement, container: ViewContainerRef) => {
2024-05-17 20:25:51 +03:00
symbolElement.stopEdit();
2024-05-17 19:31:35 +03:00
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) => {
2024-05-17 20:25:51 +03:00
symbolElement.stopEdit();
2024-05-17 19:31:35 +03:00
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) => {
2024-05-17 20:25:51 +03:00
symbolElement.startEdit(() => {
if (isAdd) {
symbolElement.unhighlight();
symbolElement.tooltip.close();
} else {
componentRef.destroy();
setupTagPanelTooltip(symbolElement, container);
}
});
2024-05-17 19:31:35 +03:00
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(() => {
2024-05-17 20:25:51 +03:00
symbolElement.stopEdit(true);
2024-05-17 19:31:35 +03:00
});
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,
2024-05-20 18:08:25 +03:00
container: ViewContainerRef,
componentType: Type<T>,
tooltip?: ITooltipsterInstance): ComponentRef<T> => {
if (!tooltip) {
tooltip = symbolElement.tooltip;
}
2024-05-17 19:31:35 +03:00
const componentRef = container.createComponent(componentType);
componentRef.instance.symbolElement = symbolElement;
componentRef.instance.viewInited.subscribe(() => {
2024-05-20 18:08:25 +03:00
if (tooltip.status().open) {
tooltip.reposition();
2024-05-17 19:31:35 +03:00
}
});
2024-05-20 18:08:25 +03:00
tooltip.on('destroyed', () => {
componentRef.destroy();
});
2024-05-17 19:31:35 +03:00
const parentElement = componentRef.instance.element.nativeElement;
const content = parentElement.firstChild;
parentElement.removeChild(content);
parentElement.style.display = 'none';
2024-05-20 18:08:25 +03:00
tooltip.content(content);
2024-05-17 19:31:35 +03:00
return componentRef;
};