11 changed files with 499 additions and 226 deletions
@ -0,0 +1,112 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2021 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. |
||||
|
|
||||
|
--> |
||||
|
<div tb-fullscreen [fullscreen]="widget.isFullscreen" |
||||
|
[fullscreenBackgroundStyle]="dashboardStyle" |
||||
|
[fullscreenBackgroundImage]="backgroundImage" |
||||
|
(fullscreenChanged)="onFullscreenChanged($event)" |
||||
|
fxLayout="column" |
||||
|
class="tb-widget" |
||||
|
[ngClass]="{ |
||||
|
'tb-highlighted': isHighlighted(widget), |
||||
|
'tb-not-highlighted': isNotHighlighted(widget), |
||||
|
'mat-elevation-z4': widget.dropShadow, |
||||
|
'tb-has-timewindow': widget.hasTimewindow |
||||
|
}" |
||||
|
[ngStyle]="widget.style" |
||||
|
(mousedown)="onMouseDown($event)" |
||||
|
(click)="onClicked($event)" |
||||
|
(contextmenu)="onContextMenu($event)"> |
||||
|
<div *ngIf="!!widgetComponent.widgetContext?.inited" fxLayout="row" fxLayoutAlign="space-between start"> |
||||
|
<div class="tb-widget-title" fxLayout="column" fxLayoutAlign="center start" *ngIf="widget.showWidgetTitlePanel"> |
||||
|
<span *ngIf="widget.showTitle" |
||||
|
[ngClass]="{'single-row': widget.hasTimewindow}" |
||||
|
[ngStyle]="widget.titleStyle" |
||||
|
[matTooltip]="widget.titleTooltip" |
||||
|
matTooltipClass="tb-tooltip-multiline" |
||||
|
matTooltipPosition="above" |
||||
|
class="mat-subheading-2 title"> |
||||
|
<mat-icon *ngIf="widget.showTitleIcon" [ngStyle]="widget.titleIconStyle">{{widget.titleIcon}}</mat-icon> |
||||
|
{{widget.customTranslatedTitle}} |
||||
|
</span> |
||||
|
<tb-timewindow *ngIf="widget.hasTimewindow" |
||||
|
aggregation="{{widget.hasAggregation}}" |
||||
|
timezone="true" |
||||
|
[isEdit]="isEdit" |
||||
|
[(ngModel)]="widgetComponent.widget.config.timewindow" |
||||
|
(ngModelChange)="widgetComponent.onTimewindowChanged($event)"> |
||||
|
</tb-timewindow> |
||||
|
</div> |
||||
|
<div *ngIf="widget.showWidgetActions" |
||||
|
class="tb-widget-actions" |
||||
|
[ngClass]="{'tb-widget-actions-absolute': !(widget.showWidgetTitlePanel&&(widget.showTitle||widget.hasAggregation))}" |
||||
|
fxLayout="row" |
||||
|
fxLayoutAlign="start center" |
||||
|
(mousedown)="$event.stopPropagation()"> |
||||
|
<button mat-button mat-icon-button *ngFor="let action of widget.customHeaderActions" |
||||
|
[fxShow]="!isEdit" |
||||
|
(click)="action.onAction($event)" |
||||
|
matTooltip="{{ action.displayName }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>{{ action.icon }}</mat-icon> |
||||
|
</button> |
||||
|
<button mat-button mat-icon-button *ngFor="let action of widget.widgetActions" |
||||
|
[fxShow]="!isEdit && action.show" |
||||
|
(click)="action.onAction($event)" |
||||
|
matTooltip="{{ action.name | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>{{ action.icon }}</mat-icon> |
||||
|
</button> |
||||
|
<button mat-button mat-icon-button |
||||
|
[fxShow]="!isEdit && widget.enableFullscreen" |
||||
|
(click)="$event.stopPropagation(); widget.isFullscreen = !widget.isFullscreen" |
||||
|
matTooltip="{{(widget.isFullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>{{ widget.isFullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> |
||||
|
</button> |
||||
|
<button mat-button mat-icon-button |
||||
|
[fxShow]="isEditActionEnabled && !widget.isFullscreen" |
||||
|
(click)="onEdit($event)" |
||||
|
matTooltip="{{ 'widget.edit' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>edit</mat-icon> |
||||
|
</button> |
||||
|
<button mat-button mat-icon-button |
||||
|
[fxShow]="isExportActionEnabled && !widget.isFullscreen" |
||||
|
(click)="onExport($event)" |
||||
|
matTooltip="{{ 'widget.export' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>file_download</mat-icon> |
||||
|
</button> |
||||
|
<button mat-button mat-icon-button |
||||
|
[fxShow]="isRemoveActionEnabled && !widget.isFullscreen" |
||||
|
(click)="onRemove($event);" |
||||
|
matTooltip="{{ 'widget.remove' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>close</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div fxFlex fxLayout="column" class="tb-widget-content" [ngClass]="{'tb-no-interaction': disableWidgetInteraction}"> |
||||
|
<tb-widget fxFlex |
||||
|
#widgetComponent |
||||
|
[dashboardWidget]="widget" |
||||
|
[isEdit]="isEdit" |
||||
|
[isMobile]="isMobile"> |
||||
|
</tb-widget> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,117 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 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. |
||||
|
*/ |
||||
|
:host { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
tb-widget.tb-widget { |
||||
|
position: relative; |
||||
|
height: 100%; |
||||
|
margin: 0; |
||||
|
overflow: hidden; |
||||
|
outline: none; |
||||
|
|
||||
|
transition: all .2s ease-in-out; |
||||
|
} |
||||
|
|
||||
|
div.tb-widget { |
||||
|
position: relative; |
||||
|
height: 100%; |
||||
|
margin: 0; |
||||
|
overflow: hidden; |
||||
|
outline: none; |
||||
|
|
||||
|
transition: all .2s ease-in-out; |
||||
|
|
||||
|
.tb-widget-title { |
||||
|
max-height: 65px; |
||||
|
padding-top: 5px; |
||||
|
padding-left: 5px; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
tb-timewindow { |
||||
|
font-size: 14px; |
||||
|
opacity: .85; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
width: 100%; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
line-height: 24px; |
||||
|
letter-spacing: .01em; |
||||
|
margin: 0; |
||||
|
display: -webkit-box; |
||||
|
-webkit-box-orient: vertical; |
||||
|
-webkit-line-clamp: 2; |
||||
|
|
||||
|
&.single-row{ |
||||
|
-webkit-line-clamp: 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-widget-actions { |
||||
|
z-index: 19; |
||||
|
margin: 5px 0 0; |
||||
|
|
||||
|
&-absolute { |
||||
|
position: absolute; |
||||
|
top: 3px; |
||||
|
right: 8px; |
||||
|
z-index: 150; |
||||
|
} |
||||
|
|
||||
|
button.mat-icon-button { |
||||
|
width: 32px; |
||||
|
min-width: 32px; |
||||
|
height: 32px; |
||||
|
min-height: 32px; |
||||
|
padding: 0 !important; |
||||
|
margin: 0 !important; |
||||
|
line-height: 20px; |
||||
|
|
||||
|
mat-icon { |
||||
|
width: 20px; |
||||
|
min-width: 20px; |
||||
|
height: 20px; |
||||
|
min-height: 20px; |
||||
|
font-size: 20px; |
||||
|
line-height: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-widget-content { |
||||
|
&.tb-no-interaction { |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
tb-widget { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.tb-highlighted { |
||||
|
border: 1px solid #039be5; |
||||
|
box-shadow: 0 0 20px #039be5; |
||||
|
} |
||||
|
|
||||
|
&.tb-not-highlighted { |
||||
|
opacity: .5; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,153 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 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 { |
||||
|
ChangeDetectionStrategy, |
||||
|
ChangeDetectorRef, |
||||
|
Component, |
||||
|
EventEmitter, |
||||
|
Input, |
||||
|
OnInit, |
||||
|
Output |
||||
|
} from '@angular/core'; |
||||
|
import { PageComponent } from '@shared/components/page.component'; |
||||
|
import { DashboardWidget, DashboardWidgets } from '@home/models/dashboard-component.models'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { SafeStyle } from '@angular/platform-browser'; |
||||
|
|
||||
|
export enum WidgetComponentActionType { |
||||
|
MOUSE_DOWN, |
||||
|
CLICKED, |
||||
|
CONTEXT_MENU, |
||||
|
EDIT, |
||||
|
EXPORT, |
||||
|
REMOVE |
||||
|
} |
||||
|
|
||||
|
export class WidgetComponentAction { |
||||
|
event: MouseEvent; |
||||
|
actionType: WidgetComponentActionType; |
||||
|
} |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-widget-container', |
||||
|
templateUrl: './widget-container.component.html', |
||||
|
styleUrls: ['./widget-container.component.scss'], |
||||
|
changeDetection: ChangeDetectionStrategy.OnPush |
||||
|
}) |
||||
|
export class WidgetContainerComponent extends PageComponent implements OnInit { |
||||
|
|
||||
|
@Input() |
||||
|
widget: DashboardWidget; |
||||
|
|
||||
|
@Input() |
||||
|
dashboardStyle: {[klass: string]: any}; |
||||
|
|
||||
|
@Input() |
||||
|
backgroundImage: SafeStyle | string; |
||||
|
|
||||
|
@Input() |
||||
|
isEdit: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
isMobile: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
dashboardWidgets: DashboardWidgets; |
||||
|
|
||||
|
@Input() |
||||
|
isEditActionEnabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
isExportActionEnabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
isRemoveActionEnabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
disableWidgetInteraction = false; |
||||
|
|
||||
|
@Output() |
||||
|
widgetFullscreenChanged: EventEmitter<boolean> = new EventEmitter<boolean>(); |
||||
|
|
||||
|
@Output() |
||||
|
widgetComponentAction: EventEmitter<WidgetComponentAction> = new EventEmitter<WidgetComponentAction>(); |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private cd: ChangeDetectorRef) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.widget.widgetContext.containerChangeDetector = this.cd; |
||||
|
} |
||||
|
|
||||
|
isHighlighted(widget: DashboardWidget) { |
||||
|
return this.dashboardWidgets.isHighlighted(widget); |
||||
|
} |
||||
|
|
||||
|
isNotHighlighted(widget: DashboardWidget) { |
||||
|
return this.dashboardWidgets.isNotHighlighted(widget); |
||||
|
} |
||||
|
|
||||
|
onFullscreenChanged(expanded: boolean) { |
||||
|
this.widgetFullscreenChanged.emit(expanded); |
||||
|
} |
||||
|
|
||||
|
onMouseDown(event: MouseEvent) { |
||||
|
this.widgetComponentAction.emit({ |
||||
|
event, |
||||
|
actionType: WidgetComponentActionType.MOUSE_DOWN |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onClicked(event: MouseEvent) { |
||||
|
this.widgetComponentAction.emit({ |
||||
|
event, |
||||
|
actionType: WidgetComponentActionType.CLICKED |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onContextMenu(event: MouseEvent) { |
||||
|
this.widgetComponentAction.emit({ |
||||
|
event, |
||||
|
actionType: WidgetComponentActionType.CONTEXT_MENU |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onEdit(event: MouseEvent) { |
||||
|
this.widgetComponentAction.emit({ |
||||
|
event, |
||||
|
actionType: WidgetComponentActionType.EDIT |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onExport(event: MouseEvent) { |
||||
|
this.widgetComponentAction.emit({ |
||||
|
event, |
||||
|
actionType: WidgetComponentActionType.EXPORT |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onRemove(event: MouseEvent) { |
||||
|
this.widgetComponentAction.emit({ |
||||
|
event, |
||||
|
actionType: WidgetComponentActionType.REMOVE |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue