37 changed files with 1830 additions and 170 deletions
@ -0,0 +1,65 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2019 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 class="mat-content" style="position: relative; width: 100%; height: 100%;" |
||||
|
[ngStyle]="{'background-color': layoutCtx.gridSettings.backgroundColor, |
||||
|
'background-image': layoutCtx.gridSettings.backgroundImageUrl ? |
||||
|
'url('+layoutCtx.gridSettings.backgroundImageUrl+')' : 'none', |
||||
|
'background-repeat': 'no-repeat', |
||||
|
'background-attachment': 'scroll', |
||||
|
'background-size': layoutCtx.gridSettings.backgroundSizeMode || '100%', |
||||
|
'background-position': '0% 0%'}"> |
||||
|
<section *ngIf="layoutCtx.widgets.length === 0" fxLayoutAlign="center center" |
||||
|
[ngStyle]="{'color': layoutCtx.gridSettings.color}" |
||||
|
style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;" |
||||
|
class="mat-headline tb-absolute-fill"> |
||||
|
<span *ngIf="!isEdit"> |
||||
|
{{'dashboard.no-widgets' | translate}} |
||||
|
</span> |
||||
|
<button mat-button *ngIf="isEdit && !widgetEditMode" class="tb-add-new-widget" |
||||
|
(click)="addWidget($event)"> |
||||
|
<mat-icon class="tb-mat-96">add</mat-icon> |
||||
|
{{ 'dashboard.add-widget' | translate }} |
||||
|
</button> |
||||
|
</section> |
||||
|
<tb-dashboard #dashboard [dashboardStyle]="{'background-color': layoutCtx.gridSettings.backgroundColor, |
||||
|
'background-image': layoutCtx.gridSettings.backgroundImageUrl ? |
||||
|
'url('+layoutCtx.gridSettings.backgroundImageUrl+')' : 'none', |
||||
|
'background-repeat': 'no-repeat', |
||||
|
'background-attachment': 'scroll', |
||||
|
'background-size': layoutCtx.gridSettings.backgroundSizeMode || '100%', |
||||
|
'background-position': '0% 0%'}" |
||||
|
[widgets]="layoutCtx.widgets" |
||||
|
[widgetLayouts]="layoutCtx.widgetLayouts" |
||||
|
[columns]="layoutCtx.gridSettings.columns" |
||||
|
[horizontalMargin]="layoutCtx.gridSettings.margins ? layoutCtx.gridSettings.margins[0] : 10" |
||||
|
[verticalMargin]="layoutCtx.gridSettings.margins ? layoutCtx.gridSettings.margins[1]: 10" |
||||
|
[aliasController]="dashboardCtx.aliasController" |
||||
|
[stateController]="dashboardCtx.stateController" |
||||
|
[dashboardTimewindow]="dashboardCtx.dashboardTimewindow" |
||||
|
[isEdit]="isEdit" |
||||
|
[autofillHeight]="layoutCtx.gridSettings.autoFillHeight && !isEdit" |
||||
|
[mobileAutofillHeight]="layoutCtx.gridSettings.mobileAutoFillHeight && !isEdit" |
||||
|
[mobileRowHeight]="layoutCtx.gridSettings.mobileRowHeight" |
||||
|
[isMobile]="isMobile" |
||||
|
[isMobileDisabled]="widgetEditMode" |
||||
|
[isEditActionEnabled]="isEdit" |
||||
|
[isExportActionEnabled]="isEdit && !widgetEditMode" |
||||
|
[isRemoveActionEnabled]="isEdit && !widgetEditMode" |
||||
|
[ignoreLoading]="layoutCtx.ignoreLoading"> |
||||
|
</tb-dashboard> |
||||
|
</div> |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2019 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 { |
||||
|
button.tb-add-new-widget { |
||||
|
padding-right: 12px; |
||||
|
font-size: 24px; |
||||
|
border-style: dashed; |
||||
|
border-width: 2px; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { Component, OnDestroy, OnInit, Input, ChangeDetectorRef, ViewChild } from '@angular/core'; |
||||
|
import { StateControllerComponent } from '@home/pages/dashboard/states/state-controller.component'; |
||||
|
import { ILayoutController } from '@home/pages/dashboard/layout/layout.models'; |
||||
|
import { DashboardContext, DashboardPageLayoutContext } from '@home/pages/dashboard/dashboard-page.models'; |
||||
|
import { PageComponent } from '@shared/components/page.component'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { Widget } from '@shared/models/widget.models'; |
||||
|
import { WidgetLayouts } from '@shared/models/dashboard.models'; |
||||
|
import { GridsterComponent } from 'angular-gridster2'; |
||||
|
import { IDashboardComponent } from '@home/models/dashboard-component.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-dashboard-layout', |
||||
|
templateUrl: './dashboard-layout.component.html', |
||||
|
styleUrls: ['./dashboard-layout.component.scss'] |
||||
|
}) |
||||
|
export class DashboardLayoutComponent extends PageComponent implements ILayoutController, OnInit, OnDestroy { |
||||
|
|
||||
|
layoutCtxValue: DashboardPageLayoutContext; |
||||
|
|
||||
|
@Input() |
||||
|
set layoutCtx(val: DashboardPageLayoutContext) { |
||||
|
this.layoutCtxValue = val; |
||||
|
if (this.layoutCtxValue) { |
||||
|
this.layoutCtxValue.ctrl = this; |
||||
|
} |
||||
|
} |
||||
|
get layoutCtx(): DashboardPageLayoutContext { |
||||
|
return this.layoutCtxValue; |
||||
|
} |
||||
|
|
||||
|
@Input() |
||||
|
dashboardCtx: DashboardContext; |
||||
|
|
||||
|
@Input() |
||||
|
isEdit: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
isMobile: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
widgetEditMode: boolean; |
||||
|
|
||||
|
@ViewChild('dashboard', {static: true}) dashboard: IDashboardComponent; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private cd: ChangeDetectorRef) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy(): void { |
||||
|
} |
||||
|
|
||||
|
reload() { |
||||
|
} |
||||
|
|
||||
|
setResizing(layoutVisibilityChanged: boolean) { |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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.
|
||||
|
///
|
||||
|
|
||||
|
export interface ILayoutController { |
||||
|
reload(); |
||||
|
setResizing(layoutVisibilityChanged: boolean); |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2019 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. |
||||
|
|
||||
|
--> |
||||
|
<mat-select class="default-state-controller" [fxShow]="displayStateSelection()" |
||||
|
[(ngModel)]="stateObject[0].id" (ngModelChange)="selectedStateIdChanged()"> |
||||
|
<mat-option *ngFor="let stateKv of states | keyvalue" [value]="stateKv.key"> |
||||
|
{{getStateName(stateKv.key, stateKv.value)}} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
@ -0,0 +1,20 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2019 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 { |
||||
|
mat-select.default-state-controller { |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,255 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { |
||||
|
Component, |
||||
|
OnInit, |
||||
|
ViewEncapsulation, |
||||
|
Input, |
||||
|
OnDestroy, |
||||
|
OnChanges, |
||||
|
SimpleChanges, |
||||
|
NgZone |
||||
|
} from '@angular/core'; |
||||
|
import { IStateController, StateParams, StateObject } from '@core/api/widget-api.models'; |
||||
|
import { ActivatedRoute, Router } from '@angular/router'; |
||||
|
import { Observable, Subscription, of } from 'rxjs'; |
||||
|
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models'; |
||||
|
import { DashboardState } from '@shared/models/dashboard.models'; |
||||
|
import { IStateControllerComponent, StateControllerState } from './state-controller.models'; |
||||
|
import { StateControllerComponent } from './state-controller.component'; |
||||
|
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; |
||||
|
import { EntityId } from '@app/shared/models/id/entity-id'; |
||||
|
import { UtilsService } from '@core/services/utils.service'; |
||||
|
import { base64toObj, objToBase64 } from '@app/core/utils'; |
||||
|
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; |
||||
|
import { EntityService } from '@core/http/entity.service'; |
||||
|
import { EntityType } from '@shared/models/entity-type.models'; |
||||
|
import { map } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-default-state-controller', |
||||
|
templateUrl: './default-state-controller.component.html', |
||||
|
styleUrls: ['./default-state-controller.component.scss'] |
||||
|
}) |
||||
|
export class DefaultStateControllerComponent extends StateControllerComponent implements OnInit, OnDestroy { |
||||
|
|
||||
|
constructor(protected router: Router, |
||||
|
protected route: ActivatedRoute, |
||||
|
protected statesControllerService: StatesControllerService, |
||||
|
private utils: UtilsService, |
||||
|
private entityService: EntityService, |
||||
|
private dashboardUtils: DashboardUtilsService) { |
||||
|
super(router, route, statesControllerService); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
super.ngOnInit(); |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy(): void { |
||||
|
super.ngOnDestroy(); |
||||
|
} |
||||
|
|
||||
|
protected init() { |
||||
|
if (this.preservedState) { |
||||
|
this.stateObject = this.preservedState; |
||||
|
setTimeout(() => { |
||||
|
this.gotoState(this.stateObject[0].id, true); |
||||
|
}, 1); |
||||
|
} else { |
||||
|
const initialState = this.currentState; |
||||
|
this.stateObject = this.parseState(initialState); |
||||
|
setTimeout(() => { |
||||
|
this.gotoState(this.stateObject[0].id, false); |
||||
|
}, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected onMobileChanged() { |
||||
|
} |
||||
|
|
||||
|
protected onStateIdChanged() { |
||||
|
} |
||||
|
|
||||
|
protected onStatesChanged() { |
||||
|
} |
||||
|
|
||||
|
protected onStateChanged() { |
||||
|
this.stateObject = this.parseState(this.currentState); |
||||
|
this.gotoState(this.stateObject[0].id, true); |
||||
|
} |
||||
|
|
||||
|
protected stateControllerId(): string { |
||||
|
return 'default'; |
||||
|
} |
||||
|
|
||||
|
public getStateParams(): StateParams { |
||||
|
if (this.stateObject && this.stateObject.length) { |
||||
|
return this.stateObject[this.stateObject.length - 1].params; |
||||
|
} else { |
||||
|
return {}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public openState(id: string, params?: StateParams, openRightLayout?: boolean): void { |
||||
|
if (this.states && this.states[id]) { |
||||
|
if (!params) { |
||||
|
params = {}; |
||||
|
} |
||||
|
const newState: StateObject = { |
||||
|
id, |
||||
|
params |
||||
|
}; |
||||
|
this.stateObject[0] = newState; |
||||
|
this.gotoState(this.stateObject[0].id, true, openRightLayout); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public updateState(id: string, params?: StateParams, openRightLayout?: boolean): void { |
||||
|
if (!id) { |
||||
|
id = this.getStateId(); |
||||
|
} |
||||
|
if (this.states && this.states[id]) { |
||||
|
if (!params) { |
||||
|
params = {}; |
||||
|
} |
||||
|
const newState: StateObject = { |
||||
|
id, |
||||
|
params |
||||
|
}; |
||||
|
this.stateObject[0] = newState; |
||||
|
this.gotoState(this.stateObject[0].id, true, openRightLayout); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getEntityId(entityParamName: string): EntityId { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public getStateId(): string { |
||||
|
if (this.stateObject && this.stateObject.length) { |
||||
|
return this.stateObject[this.stateObject.length - 1].id; |
||||
|
} else { |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getStateIdAtIndex(index: number): string { |
||||
|
if (this.stateObject && this.stateObject[index]) { |
||||
|
return this.stateObject[index].id; |
||||
|
} else { |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getStateIndex(): number { |
||||
|
if (this.stateObject && this.stateObject.length) { |
||||
|
return this.stateObject.length - 1; |
||||
|
} else { |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getStateParamsByStateId(stateId: string): StateParams { |
||||
|
const stateObj = this.getStateObjById(stateId); |
||||
|
if (stateObj) { |
||||
|
return stateObj.params; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public navigatePrevState(index: number): void { |
||||
|
if (index < this.stateObject.length - 1) { |
||||
|
this.stateObject.splice(index + 1, this.stateObject.length - index - 1); |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public resetState(): void { |
||||
|
const rootStateId = this.dashboardUtils.getRootStateId(this.states); |
||||
|
this.stateObject = [ { id: rootStateId, params: {} } ]; |
||||
|
this.gotoState(rootStateId, true); |
||||
|
} |
||||
|
|
||||
|
public getStateName(id: string, state: DashboardState): string { |
||||
|
return this.utils.customTranslation(state.name, id); |
||||
|
} |
||||
|
|
||||
|
public displayStateSelection(): boolean { |
||||
|
return this.states && Object.keys(this.states).length > 1; |
||||
|
} |
||||
|
|
||||
|
public selectedStateIdChanged() { |
||||
|
this.gotoState(this.stateObject[0].id, true); |
||||
|
} |
||||
|
|
||||
|
private parseState(stateBase64: string): StateControllerState { |
||||
|
let result: StateControllerState; |
||||
|
if (stateBase64) { |
||||
|
try { |
||||
|
result = base64toObj(stateBase64); |
||||
|
} catch (e) { |
||||
|
result = [ { id: null, params: {} } ]; |
||||
|
} |
||||
|
} |
||||
|
if (!result) { |
||||
|
result = []; |
||||
|
} |
||||
|
if (!result.length) { |
||||
|
result[0] = { id: null, params: {} }; |
||||
|
} else if (result.length > 1) { |
||||
|
const newResult = []; |
||||
|
newResult.push(result[result.length - 1]); |
||||
|
result = newResult; |
||||
|
} |
||||
|
const rootStateId = this.dashboardUtils.getRootStateId(this.states); |
||||
|
if (!result[0].id) { |
||||
|
result[0].id = rootStateId; |
||||
|
} |
||||
|
if (!this.states[result[0].id]) { |
||||
|
result[0].id = rootStateId; |
||||
|
} |
||||
|
let i = result.length; |
||||
|
while (i--) { |
||||
|
if (!result[i].id || !this.states[result[i].id]) { |
||||
|
result.splice(i, 1); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private gotoState(stateId: string, update: boolean, openRightLayout?: boolean) { |
||||
|
if (this.dashboardCtrl.dashboardCtx.state !== stateId) { |
||||
|
this.dashboardCtrl.openDashboardState(stateId, openRightLayout); |
||||
|
if (update) { |
||||
|
this.updateLocation(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private updateLocation() { |
||||
|
if (this.stateObject[0].id) { |
||||
|
const newState = objToBase64(this.stateObject); |
||||
|
this.updateStateParam(newState); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private getStateObjById(id: string): StateObject { |
||||
|
return this.stateObject.find((stateObj) => stateObj.id === id); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2019 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 class="entity-state-controller" fxLayout="row" fxLayoutAlign="start center"> |
||||
|
<div *ngIf="!isMobile || stateObject.length === 1" fxLayout="row" fxLayoutAlign="start center"> |
||||
|
<span *ngFor="let state of stateObject; index as i; last as isLast" class="state-entry" [ngStyle]="{fontWeight: isLast ? 'bold' : 'normal', |
||||
|
cursor: isLast ? 'default' : 'pointer'}" (click)="navigatePrevState(i)"> |
||||
|
{{getStateName(i)}} |
||||
|
<span class='state-divider' [fxHide]="isLast"> > </span> |
||||
|
</span> |
||||
|
</div> |
||||
|
<mat-select *ngIf="isMobile && stateObject.length > 1" |
||||
|
[(ngModel)]="selectedStateIndex" (ngModelChange)="selectedStateIndexChanged()"> |
||||
|
<mat-option *ngFor="let state of stateObject; index as i" [value]="i"> |
||||
|
{{getStateName(i)}} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,45 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2019 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 { |
||||
|
.entity-state-controller { |
||||
|
.state-divider { |
||||
|
padding-right: 15px; |
||||
|
padding-left: 15px; |
||||
|
overflow: hidden; |
||||
|
font-size: 18px; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
|
||||
|
.state-entry { |
||||
|
overflow: hidden; |
||||
|
font-size: 18px; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
outline: none; |
||||
|
} |
||||
|
|
||||
|
mat-select { |
||||
|
margin: 0; |
||||
|
|
||||
|
.mat-select-value-text { |
||||
|
font-size: 18px; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,316 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { |
||||
|
Component, |
||||
|
OnInit, |
||||
|
ViewEncapsulation, |
||||
|
Input, |
||||
|
OnDestroy, |
||||
|
OnChanges, |
||||
|
SimpleChanges, |
||||
|
NgZone |
||||
|
} from '@angular/core'; |
||||
|
import { IStateController, StateParams, StateObject } from '@core/api/widget-api.models'; |
||||
|
import { ActivatedRoute, Router } from '@angular/router'; |
||||
|
import { Observable, Subscription, of } from 'rxjs'; |
||||
|
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models'; |
||||
|
import { DashboardState } from '@shared/models/dashboard.models'; |
||||
|
import { IStateControllerComponent, StateControllerState } from './state-controller.models'; |
||||
|
import { StateControllerComponent } from './state-controller.component'; |
||||
|
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; |
||||
|
import { EntityId } from '@app/shared/models/id/entity-id'; |
||||
|
import { UtilsService } from '@core/services/utils.service'; |
||||
|
import { base64toObj, objToBase64 } from '@app/core/utils'; |
||||
|
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; |
||||
|
import { EntityService } from '@core/http/entity.service'; |
||||
|
import { EntityType } from '@shared/models/entity-type.models'; |
||||
|
import { map } from 'rxjs/operators'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-entity-state-controller', |
||||
|
templateUrl: './entity-state-controller.component.html', |
||||
|
styleUrls: ['./entity-state-controller.component.scss'] |
||||
|
}) |
||||
|
export class EntityStateControllerComponent extends StateControllerComponent implements OnInit, OnDestroy { |
||||
|
|
||||
|
private selectedStateIndex = -1; |
||||
|
|
||||
|
constructor(protected router: Router, |
||||
|
protected route: ActivatedRoute, |
||||
|
protected statesControllerService: StatesControllerService, |
||||
|
private utils: UtilsService, |
||||
|
private entityService: EntityService, |
||||
|
private dashboardUtils: DashboardUtilsService) { |
||||
|
super(router, route, statesControllerService); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
super.ngOnInit(); |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy(): void { |
||||
|
super.ngOnDestroy(); |
||||
|
} |
||||
|
|
||||
|
protected init() { |
||||
|
if (this.preservedState) { |
||||
|
this.stateObject = this.preservedState; |
||||
|
this.selectedStateIndex = this.stateObject.length - 1; |
||||
|
setTimeout(() => { |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, true); |
||||
|
}, 1); |
||||
|
} else { |
||||
|
const initialState = this.currentState; |
||||
|
this.stateObject = this.parseState(initialState); |
||||
|
this.selectedStateIndex = this.stateObject.length - 1; |
||||
|
setTimeout(() => { |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, false); |
||||
|
}, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected onMobileChanged() { |
||||
|
} |
||||
|
|
||||
|
protected onStateIdChanged() { |
||||
|
} |
||||
|
|
||||
|
protected onStatesChanged() { |
||||
|
} |
||||
|
|
||||
|
protected onStateChanged() { |
||||
|
this.stateObject = this.parseState(this.currentState); |
||||
|
this.selectedStateIndex = this.stateObject.length - 1; |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, true); |
||||
|
} |
||||
|
|
||||
|
protected stateControllerId(): string { |
||||
|
return 'entity'; |
||||
|
} |
||||
|
|
||||
|
public getStateParams(): StateParams { |
||||
|
if (this.stateObject && this.stateObject.length) { |
||||
|
return this.stateObject[this.stateObject.length - 1].params; |
||||
|
} else { |
||||
|
return {}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public openState(id: string, params?: StateParams, openRightLayout?: boolean): void { |
||||
|
if (this.states && this.states[id]) { |
||||
|
this.resolveEntity(params).subscribe( |
||||
|
(entityName) => { |
||||
|
params.entityName = entityName; |
||||
|
const newState: StateObject = { |
||||
|
id, |
||||
|
params |
||||
|
}; |
||||
|
this.stateObject.push(newState); |
||||
|
this.selectedStateIndex = this.stateObject.length - 1; |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, true, openRightLayout); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public updateState(id: string, params?: StateParams, openRightLayout?: boolean): void { |
||||
|
if (!id) { |
||||
|
id = this.getStateId(); |
||||
|
} |
||||
|
if (this.states && this.states[id]) { |
||||
|
this.resolveEntity(params).subscribe( |
||||
|
(entityName) => { |
||||
|
params.entityName = entityName; |
||||
|
const newState: StateObject = { |
||||
|
id, |
||||
|
params |
||||
|
}; |
||||
|
this.stateObject[this.stateObject.length - 1] = newState; |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, true, openRightLayout); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getEntityId(entityParamName: string): EntityId { |
||||
|
const stateParams = this.getStateParams(); |
||||
|
if (!entityParamName || !entityParamName.length) { |
||||
|
return stateParams.entityId; |
||||
|
} else if (stateParams[entityParamName]) { |
||||
|
return stateParams[entityParamName].entityId; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public getStateId(): string { |
||||
|
if (this.stateObject && this.stateObject.length) { |
||||
|
return this.stateObject[this.stateObject.length - 1].id; |
||||
|
} else { |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getStateIdAtIndex(index: number): string { |
||||
|
if (this.stateObject && this.stateObject[index]) { |
||||
|
return this.stateObject[index].id; |
||||
|
} else { |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getStateIndex(): number { |
||||
|
if (this.stateObject && this.stateObject.length) { |
||||
|
return this.stateObject.length - 1; |
||||
|
} else { |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public getStateParamsByStateId(stateId: string): StateParams { |
||||
|
const stateObj = this.getStateObjById(stateId); |
||||
|
if (stateObj) { |
||||
|
return stateObj.params; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public navigatePrevState(index: number): void { |
||||
|
if (index < this.stateObject.length - 1) { |
||||
|
this.stateObject.splice(index + 1, this.stateObject.length - index - 1); |
||||
|
this.selectedStateIndex = this.stateObject.length - 1; |
||||
|
this.gotoState(this.stateObject[this.stateObject.length - 1].id, true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public resetState(): void { |
||||
|
const rootStateId = this.dashboardUtils.getRootStateId(this.states); |
||||
|
this.stateObject = [ { id: rootStateId, params: {} } ]; |
||||
|
this.gotoState(rootStateId, true); |
||||
|
} |
||||
|
|
||||
|
public getStateName(index: number): string { |
||||
|
let result = ''; |
||||
|
if (this.stateObject[index]) { |
||||
|
let stateName = this.states[this.stateObject[index].id].name; |
||||
|
stateName = this.utils.customTranslation(stateName, stateName); |
||||
|
const params = this.stateObject[index].params; |
||||
|
const entityName = params && params.entityName ? params.entityName : ''; |
||||
|
result = this.utils.insertVariable(stateName, 'entityName', entityName); |
||||
|
for (const prop of Object.keys(params)) { |
||||
|
if (params[prop] && params[prop].entityName) { |
||||
|
result = this.utils.insertVariable(result, prop + ':entityName', params[prop].entityName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public selectedStateIndexChanged() { |
||||
|
this.navigatePrevState(this.selectedStateIndex); |
||||
|
} |
||||
|
|
||||
|
private parseState(stateBase64: string): StateControllerState { |
||||
|
let result: StateControllerState; |
||||
|
if (stateBase64) { |
||||
|
try { |
||||
|
result = base64toObj(stateBase64); |
||||
|
} catch (e) { |
||||
|
result = [ { id: null, params: {} } ]; |
||||
|
} |
||||
|
} |
||||
|
if (!result) { |
||||
|
result = []; |
||||
|
} |
||||
|
if (!result.length) { |
||||
|
result[0] = { id: null, params: {} }; |
||||
|
} |
||||
|
const rootStateId = this.dashboardUtils.getRootStateId(this.states); |
||||
|
if (!result[0].id) { |
||||
|
result[0].id = rootStateId; |
||||
|
} |
||||
|
if (!this.states[result[0].id]) { |
||||
|
result[0].id = rootStateId; |
||||
|
} |
||||
|
let i = result.length; |
||||
|
while (i--) { |
||||
|
if (!result[i].id || !this.states[result[i].id]) { |
||||
|
result.splice(i, 1); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private gotoState(stateId: string, update: boolean, openRightLayout?: boolean) { |
||||
|
this.dashboardCtrl.openDashboardState(stateId, openRightLayout); |
||||
|
if (update) { |
||||
|
this.updateLocation(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private updateLocation() { |
||||
|
if (this.stateObject[this.stateObject.length - 1].id) { |
||||
|
let newState; |
||||
|
if (this.isDefaultState()) { |
||||
|
newState = null; |
||||
|
} else { |
||||
|
newState = objToBase64(this.stateObject); |
||||
|
} |
||||
|
this.updateStateParam(newState); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private isDefaultState(): boolean { |
||||
|
if (this.stateObject.length === 1) { |
||||
|
const state = this.stateObject[0]; |
||||
|
const rootStateId = this.dashboardUtils.getRootStateId(this.states); |
||||
|
if (state.id === rootStateId && (!state.params || this.isEmpty(state.params))) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private isEmpty(obj: any): boolean { |
||||
|
for (const key of Object.keys(obj)) { |
||||
|
return !Object.prototype.hasOwnProperty.call(obj, key); |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private resolveEntity(params: StateParams): Observable<string> { |
||||
|
if (params && params.targetEntityParamName) { |
||||
|
params = params[params.targetEntityParamName]; |
||||
|
} |
||||
|
if (params && params.entityId && params.entityId.id && params.entityId.entityType) { |
||||
|
if (params.entityName && params.entityName.length) { |
||||
|
return of(params.entityName); |
||||
|
} else { |
||||
|
return this.entityService.getEntity(params.entityId.entityType as EntityType, |
||||
|
params.entityId.id, true, true).pipe( |
||||
|
map((entity) => entity.name) |
||||
|
); |
||||
|
} |
||||
|
} else { |
||||
|
return of(''); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private getStateObjById(id: string): StateObject { |
||||
|
return this.stateObject.find((stateObj) => stateObj.id === id); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,173 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { IStateControllerComponent, StateControllerState } from '@home/pages/dashboard/states/state-controller.models'; |
||||
|
import { IDashboardController } from '../dashboard-page.models'; |
||||
|
import { DashboardState } from '@app/shared/models/dashboard.models'; |
||||
|
import { Subscription } from 'rxjs'; |
||||
|
import { OnDestroy, OnInit } from '@angular/core'; |
||||
|
import { ActivatedRoute, Router, Params } from '@angular/router'; |
||||
|
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; |
||||
|
import { EntityId } from '@app/shared/models/id/entity-id'; |
||||
|
import { StateParams } from '@app/core/api/widget-api.models'; |
||||
|
|
||||
|
export abstract class StateControllerComponent implements IStateControllerComponent, OnInit, OnDestroy { |
||||
|
|
||||
|
stateObject: StateControllerState = []; |
||||
|
dashboardCtrl: IDashboardController; |
||||
|
preservedState: any; |
||||
|
|
||||
|
isMobileValue: boolean; |
||||
|
set isMobile(val: boolean) { |
||||
|
if (this.isMobileValue !== val) { |
||||
|
this.isMobileValue = val; |
||||
|
if (this.inited) { |
||||
|
this.onMobileChanged(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
get isMobile(): boolean { |
||||
|
return this.isMobileValue; |
||||
|
} |
||||
|
|
||||
|
stateValue: string; |
||||
|
set state(val: string) { |
||||
|
if (this.stateValue !== val) { |
||||
|
this.stateValue = val; |
||||
|
if (this.inited) { |
||||
|
this.onStateIdChanged(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
get state(): string { |
||||
|
return this.stateValue; |
||||
|
} |
||||
|
|
||||
|
dashboardIdValue: string; |
||||
|
set dashboardId(val: string) { |
||||
|
if (this.dashboardIdValue !== val) { |
||||
|
this.dashboardIdValue = val; |
||||
|
if (this.inited) { |
||||
|
this.init(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
get dashboardId(): string { |
||||
|
return this.dashboardIdValue; |
||||
|
} |
||||
|
|
||||
|
statesValue: { [id: string]: DashboardState }; |
||||
|
set states(val: { [id: string]: DashboardState }) { |
||||
|
if (this.statesValue !== val) { |
||||
|
this.statesValue = val; |
||||
|
if (this.inited) { |
||||
|
this.onStatesChanged(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
get states(): { [id: string]: DashboardState } { |
||||
|
return this.statesValue; |
||||
|
} |
||||
|
|
||||
|
currentState: string; |
||||
|
|
||||
|
private rxSubscriptions = new Array<Subscription>(); |
||||
|
|
||||
|
private inited = false; |
||||
|
|
||||
|
constructor(protected router: Router, |
||||
|
protected route: ActivatedRoute, |
||||
|
protected statesControllerService: StatesControllerService) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.rxSubscriptions.push(this.route.queryParamMap.subscribe((paramMap) => { |
||||
|
const newState = paramMap.get('state'); |
||||
|
if (this.currentState !== newState) { |
||||
|
this.currentState = newState; |
||||
|
if (this.inited) { |
||||
|
this.onStateChanged(); |
||||
|
} |
||||
|
} |
||||
|
})); |
||||
|
this.init(); |
||||
|
this.inited = true; |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy(): void { |
||||
|
this.rxSubscriptions.forEach((subscription) => { |
||||
|
subscription.unsubscribe(); |
||||
|
}); |
||||
|
this.rxSubscriptions.length = 0; |
||||
|
} |
||||
|
|
||||
|
protected updateStateParam(newState: string) { |
||||
|
this.currentState = newState; |
||||
|
const queryParams: Params = { state: this.currentState }; |
||||
|
this.router.navigate( |
||||
|
[], |
||||
|
{ |
||||
|
relativeTo: this.route, |
||||
|
queryParams, |
||||
|
queryParamsHandling: 'merge', |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public openRightLayout(): void { |
||||
|
this.dashboardCtrl.openRightLayout(); |
||||
|
} |
||||
|
|
||||
|
public preserveState() { |
||||
|
this.statesControllerService.preserveStateControllerState(this.stateControllerId(), this.stateObject); |
||||
|
} |
||||
|
|
||||
|
public cleanupPreservedStates() { |
||||
|
this.statesControllerService.cleanupPreservedStates(); |
||||
|
} |
||||
|
|
||||
|
protected abstract init(); |
||||
|
|
||||
|
protected abstract onMobileChanged(); |
||||
|
|
||||
|
protected abstract onStateIdChanged(); |
||||
|
|
||||
|
protected abstract onStatesChanged(); |
||||
|
|
||||
|
protected abstract onStateChanged(); |
||||
|
|
||||
|
protected abstract stateControllerId(): string; |
||||
|
|
||||
|
public abstract getEntityId(entityParamName: string): EntityId; |
||||
|
|
||||
|
public abstract getStateId(): string; |
||||
|
|
||||
|
public abstract getStateIdAtIndex(index: number): string; |
||||
|
|
||||
|
public abstract getStateIndex(): number; |
||||
|
|
||||
|
public abstract getStateParams(): StateParams; |
||||
|
|
||||
|
public abstract getStateParamsByStateId(stateId: string): StateParams; |
||||
|
|
||||
|
public abstract navigatePrevState(index: number): void; |
||||
|
|
||||
|
public abstract openState(id: string, params?: StateParams, openRightLayout?: boolean): void; |
||||
|
|
||||
|
public abstract resetState(): void; |
||||
|
|
||||
|
public abstract updateState(id?: string, params?: StateParams, openRightLayout?: boolean): void; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { IStateController, StateObject } from '@core/api/widget-api.models'; |
||||
|
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models'; |
||||
|
import { DashboardState } from '@shared/models/dashboard.models'; |
||||
|
|
||||
|
export declare type StateControllerState = StateObject[]; |
||||
|
|
||||
|
export interface IStateControllerComponent extends IStateController { |
||||
|
state: string; |
||||
|
isMobile: boolean; |
||||
|
dashboardCtrl: IDashboardController; |
||||
|
states: {[id: string]: DashboardState }; |
||||
|
dashboardId: string; |
||||
|
preservedState: any; |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { |
||||
|
ComponentRef, |
||||
|
Directive, |
||||
|
ElementRef, |
||||
|
Input, |
||||
|
OnChanges, |
||||
|
OnInit, |
||||
|
OnDestroy, |
||||
|
SimpleChanges, |
||||
|
ViewContainerRef, |
||||
|
ChangeDetectorRef |
||||
|
} from '@angular/core'; |
||||
|
import { DashboardPageComponent } from '@home/pages/dashboard/dashboard-page.component'; |
||||
|
import { DashboardState } from '@shared/models/dashboard.models'; |
||||
|
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models'; |
||||
|
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; |
||||
|
import { IStateController } from '@core/api/widget-api.models'; |
||||
|
import { IStateControllerComponent } from '@home/pages/dashboard/states/state-controller.models'; |
||||
|
|
||||
|
@Directive({ |
||||
|
selector: 'tb-states-component' |
||||
|
}) |
||||
|
export class StatesComponentDirective implements OnInit, OnDestroy, OnChanges { |
||||
|
|
||||
|
@Input() |
||||
|
statesControllerId: string; |
||||
|
|
||||
|
@Input() |
||||
|
dashboardCtrl: IDashboardController; |
||||
|
|
||||
|
@Input() |
||||
|
dashboardId: string; |
||||
|
|
||||
|
@Input() |
||||
|
states: {[id: string]: DashboardState }; |
||||
|
|
||||
|
@Input() |
||||
|
state: string; |
||||
|
|
||||
|
@Input() |
||||
|
isMobile: boolean; |
||||
|
|
||||
|
stateControllerComponentRef: ComponentRef<IStateControllerComponent>; |
||||
|
stateControllerComponent: IStateControllerComponent; |
||||
|
|
||||
|
constructor(private viewContainerRef: ViewContainerRef, |
||||
|
private statesControllerService: StatesControllerService) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.init(); |
||||
|
} |
||||
|
|
||||
|
ngOnDestroy(): void { |
||||
|
this.destroy(); |
||||
|
} |
||||
|
|
||||
|
ngOnChanges(changes: SimpleChanges): void { |
||||
|
for (const propName of Object.keys(changes)) { |
||||
|
const change = changes[propName]; |
||||
|
if (!change.firstChange && change.currentValue !== change.previousValue) { |
||||
|
if (propName === 'statesControllerId') { |
||||
|
this.reInit(); |
||||
|
} else if (propName === 'states') { |
||||
|
this.stateControllerComponent.states = this.states; |
||||
|
} else if (propName === 'dashboardId') { |
||||
|
this.stateControllerComponent.dashboardId = this.dashboardId; |
||||
|
} else if (propName === 'isMobile') { |
||||
|
this.stateControllerComponent.isMobile = this.isMobile; |
||||
|
} else if (propName === 'state') { |
||||
|
this.stateControllerComponent.state = this.state; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private reInit() { |
||||
|
this.destroy(); |
||||
|
this.init(); |
||||
|
} |
||||
|
|
||||
|
private init() { |
||||
|
this.viewContainerRef.clear(); |
||||
|
let stateControllerData = this.statesControllerService.getStateController(this.statesControllerId); |
||||
|
if (!stateControllerData) { |
||||
|
stateControllerData = this.statesControllerService.getStateController('default'); |
||||
|
} |
||||
|
const preservedState = this.statesControllerService.withdrawStateControllerState(this.statesControllerId); |
||||
|
const stateControllerFactory = stateControllerData.factory; |
||||
|
this.stateControllerComponentRef = this.viewContainerRef.createComponent(stateControllerFactory); |
||||
|
this.stateControllerComponent = this.stateControllerComponentRef.instance; |
||||
|
this.dashboardCtrl.dashboardCtx.stateController = this.stateControllerComponent; |
||||
|
this.stateControllerComponent.preservedState = preservedState; |
||||
|
this.stateControllerComponent.dashboardCtrl = this.dashboardCtrl; |
||||
|
this.stateControllerComponent.state = this.state; |
||||
|
this.stateControllerComponent.isMobile = this.isMobile; |
||||
|
this.stateControllerComponent.states = this.states; |
||||
|
this.stateControllerComponent.dashboardId = this.dashboardId; |
||||
|
} |
||||
|
|
||||
|
private destroy() { |
||||
|
if (this.stateControllerComponentRef) { |
||||
|
this.stateControllerComponentRef.destroy(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { NgModule } from '@angular/core'; |
||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { SharedModule } from '@shared/shared.module'; |
||||
|
import { HomeComponentsModule } from '@modules/home/components/home-components.module'; |
||||
|
import { StatesControllerService } from './states-controller.service'; |
||||
|
import { EntityStateControllerComponent } from './entity-state-controller.component'; |
||||
|
import { StatesComponentDirective } from './states-component.directive'; |
||||
|
import { HomeDialogsModule } from '@app/modules/home/dialogs/home-dialogs.module'; |
||||
|
import { DefaultStateControllerComponent } from '@home/pages/dashboard/states/default-state-controller.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
entryComponents: [ |
||||
|
DefaultStateControllerComponent, |
||||
|
EntityStateControllerComponent |
||||
|
], |
||||
|
declarations: [ |
||||
|
StatesComponentDirective, |
||||
|
DefaultStateControllerComponent, |
||||
|
EntityStateControllerComponent |
||||
|
], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
SharedModule, |
||||
|
HomeComponentsModule, |
||||
|
HomeDialogsModule |
||||
|
], |
||||
|
exports: [ |
||||
|
StatesComponentDirective |
||||
|
], |
||||
|
providers: [ |
||||
|
StatesControllerService |
||||
|
] |
||||
|
}) |
||||
|
export class StatesControllerModule { |
||||
|
|
||||
|
constructor(private statesControllerService: StatesControllerService) { |
||||
|
this.statesControllerService.registerStatesController('default', DefaultStateControllerComponent); |
||||
|
this.statesControllerService.registerStatesController('entity', EntityStateControllerComponent); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2019 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 { ComponentFactory, ComponentFactoryResolver, Injectable, Type } from '@angular/core'; |
||||
|
import { deepClone } from '@core/utils'; |
||||
|
import { IStateControllerComponent } from '@home/pages/dashboard/states/state-controller.models'; |
||||
|
|
||||
|
export interface StateControllerData { |
||||
|
factory: ComponentFactory<IStateControllerComponent>; |
||||
|
state?: any; |
||||
|
} |
||||
|
|
||||
|
@Injectable() |
||||
|
export class StatesControllerService { |
||||
|
|
||||
|
statesControllers: {[stateControllerId: string]: StateControllerData} = {}; |
||||
|
|
||||
|
constructor(private componentFactoryResolver: ComponentFactoryResolver) { |
||||
|
} |
||||
|
|
||||
|
public registerStatesController(stateControllerId: string, stateControllerComponent: Type<IStateControllerComponent>): void { |
||||
|
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(stateControllerComponent); |
||||
|
this.statesControllers[stateControllerId] = { |
||||
|
factory: componentFactory |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public getStateControllers(): {[stateControllerId: string]: StateControllerData} { |
||||
|
return this.statesControllers; |
||||
|
} |
||||
|
|
||||
|
public getStateController(stateControllerId: string): StateControllerData { |
||||
|
return this.statesControllers[stateControllerId]; |
||||
|
} |
||||
|
|
||||
|
public preserveStateControllerState(stateControllerId: string, state: any) { |
||||
|
this.statesControllers[stateControllerId].state = deepClone(state); |
||||
|
} |
||||
|
|
||||
|
public withdrawStateControllerState(stateControllerId: string): any { |
||||
|
const state = this.statesControllers[stateControllerId].state; |
||||
|
this.statesControllers[stateControllerId].state = null; |
||||
|
return state; |
||||
|
} |
||||
|
|
||||
|
public cleanupPreservedStates() { |
||||
|
for (const stateControllerId of Object.keys(this.statesControllers)) { |
||||
|
this.statesControllers[stateControllerId].state = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue