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