Showing
12 changed files
with
243 additions
and
34 deletions
@@ -248,11 +248,14 @@ | @@ -248,11 +248,14 @@ | ||
248 | headerHeightPx="120" | 248 | headerHeightPx="120" |
249 | [isReadOnly]="true" | 249 | [isReadOnly]="true" |
250 | [isEdit]="false" | 250 | [isEdit]="false" |
251 | - [showBackStateIcon]="!!widgetsBundle?.title" | ||
252 | - (closeDetails)="onAddWidgetClosed()" | ||
253 | - (backStateDetails)="widgetBundleSelected(null)"> | 251 | + (closeDetails)="onAddWidgetClosed()"> |
252 | + <div class="prefix-title-buttons" [fxHide]="!widgetsBundle?.title" style="height: 28px; margin-right: 12px"> | ||
253 | + <button class="tb-mat-28" mat-icon-button type="button" (click)="widgetBundleSelected(null)"> | ||
254 | + <mat-icon>arrow_back</mat-icon> | ||
255 | + </button> | ||
256 | + </div> | ||
254 | <div class="header-pane" *ngIf="isAddingWidget"> | 257 | <div class="header-pane" *ngIf="isAddingWidget"> |
255 | - <div fxLayout="row"> | 258 | + <div fxLayout="row" fxLayoutGap="12px"> |
256 | <tb-widgets-bundle-search fxFlex | 259 | <tb-widgets-bundle-search fxFlex |
257 | [(ngModel)]="searchBundle" | 260 | [(ngModel)]="searchBundle" |
258 | placeholder="{{ (!widgetsBundle?.title ? 'widgets-bundle.search' : 'widget.search') | translate }}" | 261 | placeholder="{{ (!widgetsBundle?.title ? 'widgets-bundle.search' : 'widget.search') | translate }}" |
@@ -260,10 +263,21 @@ | @@ -260,10 +263,21 @@ | ||
260 | </tb-widgets-bundle-search> | 263 | </tb-widgets-bundle-search> |
261 | </div> | 264 | </div> |
262 | </div> | 265 | </div> |
266 | + <div class="details-buttons" *ngIf="isAddingWidget"> | ||
267 | + <button mat-button mat-icon-button type="button" | ||
268 | + *ngIf="widgetTypes.length > 1" | ||
269 | + (click)="editWidgetsTypesToDisplay($event)" | ||
270 | + matTooltip="{{ 'widget.filter' | translate }}" | ||
271 | + matTooltipPosition="above"> | ||
272 | + <mat-icon>filter_list</mat-icon> | ||
273 | + </button> | ||
274 | + </div> | ||
263 | <tb-dashboard-widget-select *ngIf="isAddingWidget" | 275 | <tb-dashboard-widget-select *ngIf="isAddingWidget" |
264 | [aliasController]="dashboardCtx.aliasController" | 276 | [aliasController]="dashboardCtx.aliasController" |
265 | [widgetsBundle]="widgetsBundle" | 277 | [widgetsBundle]="widgetsBundle" |
266 | [searchBundle]="searchBundle" | 278 | [searchBundle]="searchBundle" |
279 | + [filterWidgetTypes]="filterWidgetTypes" | ||
280 | + (widgetsTypes)="updateWidgetsTypes($event)" | ||
267 | (widgetsBundleSelected)="widgetBundleSelected($event)" | 281 | (widgetsBundleSelected)="widgetBundleSelected($event)" |
268 | (widgetSelected)="addWidgetFromType($event)"> | 282 | (widgetSelected)="addWidgetFromType($event)"> |
269 | </tb-dashboard-widget-select> | 283 | </tb-dashboard-widget-select> |
@@ -18,11 +18,14 @@ import { | @@ -18,11 +18,14 @@ import { | ||
18 | ChangeDetectorRef, | 18 | ChangeDetectorRef, |
19 | Component, | 19 | Component, |
20 | Inject, | 20 | Inject, |
21 | + Injector, | ||
21 | Input, | 22 | Input, |
22 | NgZone, | 23 | NgZone, |
23 | OnDestroy, | 24 | OnDestroy, |
24 | OnInit, | 25 | OnInit, |
26 | + StaticProvider, | ||
25 | ViewChild, | 27 | ViewChild, |
28 | + ViewContainerRef, | ||
26 | ViewEncapsulation | 29 | ViewEncapsulation |
27 | } from '@angular/core'; | 30 | } from '@angular/core'; |
28 | import { PageComponent } from '@shared/components/page.component'; | 31 | import { PageComponent } from '@shared/components/page.component'; |
@@ -58,7 +61,14 @@ import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; | @@ -58,7 +61,14 @@ import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; | ||
58 | import { MediaBreakpoints } from '@shared/models/constants'; | 61 | import { MediaBreakpoints } from '@shared/models/constants'; |
59 | import { AuthUser } from '@shared/models/user.model'; | 62 | import { AuthUser } from '@shared/models/user.model'; |
60 | import { getCurrentAuthState } from '@core/auth/auth.selectors'; | 63 | import { getCurrentAuthState } from '@core/auth/auth.selectors'; |
61 | -import { Widget, WidgetConfig, WidgetInfo, WidgetPosition, widgetTypesData } from '@app/shared/models/widget.models'; | 64 | +import { |
65 | + Widget, | ||
66 | + WidgetConfig, | ||
67 | + WidgetInfo, | ||
68 | + WidgetPosition, | ||
69 | + widgetType, | ||
70 | + widgetTypesData | ||
71 | +} from '@shared/models/widget.models'; | ||
62 | import { environment as env } from '@env/environment'; | 72 | import { environment as env } from '@env/environment'; |
63 | import { Authority } from '@shared/models/authority.enum'; | 73 | import { Authority } from '@shared/models/authority.enum'; |
64 | import { DialogService } from '@core/services/dialog.service'; | 74 | import { DialogService } from '@core/services/dialog.service'; |
@@ -80,7 +90,10 @@ import { | @@ -80,7 +90,10 @@ import { | ||
80 | import { EntityAliases } from '@app/shared/models/alias.models'; | 90 | import { EntityAliases } from '@app/shared/models/alias.models'; |
81 | import { EditWidgetComponent } from '@home/components/dashboard-page/edit-widget.component'; | 91 | import { EditWidgetComponent } from '@home/components/dashboard-page/edit-widget.component'; |
82 | import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | 92 | import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; |
83 | -import { AddWidgetDialogComponent, AddWidgetDialogData } from '@home/components/dashboard-page/add-widget-dialog.component'; | 93 | +import { |
94 | + AddWidgetDialogComponent, | ||
95 | + AddWidgetDialogData | ||
96 | +} from '@home/components/dashboard-page/add-widget-dialog.component'; | ||
84 | import { TranslateService } from '@ngx-translate/core'; | 97 | import { TranslateService } from '@ngx-translate/core'; |
85 | import { | 98 | import { |
86 | ManageDashboardLayoutsDialogComponent, | 99 | ManageDashboardLayoutsDialogComponent, |
@@ -99,6 +112,14 @@ import { ImportExportService } from '@home/components/import-export/import-expor | @@ -99,6 +112,14 @@ import { ImportExportService } from '@home/components/import-export/import-expor | ||
99 | import { AuthState } from '@app/core/auth/auth.models'; | 112 | import { AuthState } from '@app/core/auth/auth.models'; |
100 | import { FiltersDialogComponent, FiltersDialogData } from '@home/components/filter/filters-dialog.component'; | 113 | import { FiltersDialogComponent, FiltersDialogData } from '@home/components/filter/filters-dialog.component'; |
101 | import { Filters } from '@shared/models/query/query.models'; | 114 | import { Filters } from '@shared/models/query/query.models'; |
115 | +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; | ||
116 | +import { ComponentPortal } from '@angular/cdk/portal'; | ||
117 | +import { | ||
118 | + DISPLAY_WIDGET_TYPES_PANEL_DATA, | ||
119 | + DisplayWidgetTypesPanelComponent, | ||
120 | + DisplayWidgetTypesPanelData, | ||
121 | + WidgetTypes | ||
122 | +} from '@home/components/dashboard-page/widget-types-panel.component'; | ||
102 | 123 | ||
103 | // @dynamic | 124 | // @dynamic |
104 | @Component({ | 125 | @Component({ |
@@ -147,6 +168,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -147,6 +168,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
147 | isAddingWidgetClosed = true; | 168 | isAddingWidgetClosed = true; |
148 | widgetsBundle: WidgetsBundle = null; | 169 | widgetsBundle: WidgetsBundle = null; |
149 | searchBundle = ''; | 170 | searchBundle = ''; |
171 | + widgetTypes: WidgetTypes[] = []; | ||
172 | + filterWidgetTypes: widgetType[] = null; | ||
150 | 173 | ||
151 | isToolbarOpened = false; | 174 | isToolbarOpened = false; |
152 | isToolbarOpenedAnimate = false; | 175 | isToolbarOpenedAnimate = false; |
@@ -261,6 +284,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -261,6 +284,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
261 | private dialog: MatDialog, | 284 | private dialog: MatDialog, |
262 | private translate: TranslateService, | 285 | private translate: TranslateService, |
263 | private ngZone: NgZone, | 286 | private ngZone: NgZone, |
287 | + private overlay: Overlay, | ||
288 | + private viewContainerRef: ViewContainerRef, | ||
264 | private cd: ChangeDetectorRef) { | 289 | private cd: ChangeDetectorRef) { |
265 | super(store); | 290 | super(store); |
266 | 291 | ||
@@ -1121,6 +1146,55 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -1121,6 +1146,55 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
1121 | 1146 | ||
1122 | widgetBundleSelected(bundle: WidgetsBundle){ | 1147 | widgetBundleSelected(bundle: WidgetsBundle){ |
1123 | this.widgetsBundle = bundle; | 1148 | this.widgetsBundle = bundle; |
1149 | + this.widgetTypes = []; | ||
1124 | this.searchBundle = ''; | 1150 | this.searchBundle = ''; |
1125 | } | 1151 | } |
1152 | + | ||
1153 | + updateWidgetsTypes(types: Set<widgetType>) { | ||
1154 | + this.widgetTypes = Array.from(types.values()).map(type => { | ||
1155 | + return {type, display: true}; | ||
1156 | + }); | ||
1157 | + } | ||
1158 | + | ||
1159 | + editWidgetsTypesToDisplay($event: Event) { | ||
1160 | + if ($event) { | ||
1161 | + $event.stopPropagation(); | ||
1162 | + } | ||
1163 | + const target = $event.target || $event.currentTarget; | ||
1164 | + const config = new OverlayConfig(); | ||
1165 | + config.backdropClass = 'cdk-overlay-transparent-backdrop'; | ||
1166 | + config.hasBackdrop = true; | ||
1167 | + const connectedPosition: ConnectedPosition = { | ||
1168 | + originX: 'end', | ||
1169 | + originY: 'bottom', | ||
1170 | + overlayX: 'end', | ||
1171 | + overlayY: 'top' | ||
1172 | + }; | ||
1173 | + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) | ||
1174 | + .withPositions([connectedPosition]); | ||
1175 | + | ||
1176 | + const overlayRef = this.overlay.create(config); | ||
1177 | + overlayRef.backdropClick().subscribe(() => { | ||
1178 | + overlayRef.dispose(); | ||
1179 | + }); | ||
1180 | + | ||
1181 | + const providers: StaticProvider[] = [ | ||
1182 | + { | ||
1183 | + provide: DISPLAY_WIDGET_TYPES_PANEL_DATA, | ||
1184 | + useValue: { | ||
1185 | + types: this.widgetTypes, | ||
1186 | + typesUpdated: (newTypes) => { | ||
1187 | + this.filterWidgetTypes = newTypes.filter(type => type.display).map(type => type.type); | ||
1188 | + } | ||
1189 | + } as DisplayWidgetTypesPanelData | ||
1190 | + }, | ||
1191 | + { | ||
1192 | + provide: OverlayRef, | ||
1193 | + useValue: overlayRef | ||
1194 | + } | ||
1195 | + ]; | ||
1196 | + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); | ||
1197 | + overlayRef.attach(new ComponentPortal(DisplayWidgetTypesPanelComponent, this.viewContainerRef, injector)); | ||
1198 | + this.cd.detectChanges(); | ||
1199 | + } | ||
1126 | } | 1200 | } |
@@ -19,10 +19,10 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | @@ -19,10 +19,10 @@ import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | ||
19 | import { IAliasController } from '@core/api/widget-api.models'; | 19 | import { IAliasController } from '@core/api/widget-api.models'; |
20 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 20 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
21 | import { WidgetService } from '@core/http/widget.service'; | 21 | import { WidgetService } from '@core/http/widget.service'; |
22 | -import { WidgetInfo } from '@shared/models/widget.models'; | 22 | +import { WidgetInfo, widgetType } from '@shared/models/widget.models'; |
23 | import { toWidgetInfo } from '@home/models/widget-component.models'; | 23 | import { toWidgetInfo } from '@home/models/widget-component.models'; |
24 | -import { distinctUntilChanged, map, publishReplay, refCount, switchMap } from 'rxjs/operators'; | ||
25 | -import { BehaviorSubject, Observable, of } from 'rxjs'; | 24 | +import { distinctUntilChanged, map, publishReplay, refCount, switchMap, tap } from 'rxjs/operators'; |
25 | +import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; | ||
26 | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | 26 | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
27 | import { isDefinedAndNotNull } from '@core/utils'; | 27 | import { isDefinedAndNotNull } from '@core/utils'; |
28 | 28 | ||
@@ -34,15 +34,20 @@ import { isDefinedAndNotNull } from '@core/utils'; | @@ -34,15 +34,20 @@ import { isDefinedAndNotNull } from '@core/utils'; | ||
34 | export class DashboardWidgetSelectComponent implements OnInit { | 34 | export class DashboardWidgetSelectComponent implements OnInit { |
35 | 35 | ||
36 | private search$ = new BehaviorSubject<string>(''); | 36 | private search$ = new BehaviorSubject<string>(''); |
37 | - private widgetsTypes: Observable<Array<WidgetInfo>>; | 37 | + private filterWidgetTypes$ = new BehaviorSubject<Array<widgetType>>(null); |
38 | + private widgetsInfo: Observable<Array<WidgetInfo>>; | ||
38 | private widgetsBundleValue: WidgetsBundle; | 39 | private widgetsBundleValue: WidgetsBundle; |
40 | + private widgetsType = new Set<widgetType>(); | ||
39 | 41 | ||
40 | widgets$: Observable<Array<WidgetInfo>>; | 42 | widgets$: Observable<Array<WidgetInfo>>; |
41 | widgetsBundles$: Observable<Array<WidgetsBundle>>; | 43 | widgetsBundles$: Observable<Array<WidgetsBundle>>; |
42 | 44 | ||
43 | @Input() | 45 | @Input() |
44 | set widgetsBundle(widgetBundle: WidgetsBundle) { | 46 | set widgetsBundle(widgetBundle: WidgetsBundle) { |
45 | - this.widgetsTypes = null; | 47 | + this.widgetsInfo = null; |
48 | + this.widgetsType.clear(); | ||
49 | + this.widgetsTypes.emit(this.widgetsType); | ||
50 | + this.filterWidgetTypes$.next(null); | ||
46 | this.widgetsBundleValue = widgetBundle; | 51 | this.widgetsBundleValue = widgetBundle; |
47 | } | 52 | } |
48 | 53 | ||
@@ -58,12 +63,20 @@ export class DashboardWidgetSelectComponent implements OnInit { | @@ -58,12 +63,20 @@ export class DashboardWidgetSelectComponent implements OnInit { | ||
58 | this.search$.next(search); | 63 | this.search$.next(search); |
59 | } | 64 | } |
60 | 65 | ||
66 | + @Input() | ||
67 | + set filterWidgetTypes(widgetTypes: Array<widgetType>) { | ||
68 | + this.filterWidgetTypes$.next(widgetTypes); | ||
69 | + } | ||
70 | + | ||
61 | @Output() | 71 | @Output() |
62 | widgetSelected: EventEmitter<WidgetInfo> = new EventEmitter<WidgetInfo>(); | 72 | widgetSelected: EventEmitter<WidgetInfo> = new EventEmitter<WidgetInfo>(); |
63 | 73 | ||
64 | @Output() | 74 | @Output() |
65 | widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>(); | 75 | widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>(); |
66 | 76 | ||
77 | + @Output() | ||
78 | + widgetsTypes: EventEmitter<Set<widgetType>> = new EventEmitter<Set<widgetType>>(); | ||
79 | + | ||
67 | constructor(private widgetsService: WidgetService, | 80 | constructor(private widgetsService: WidgetService, |
68 | private sanitizer: DomSanitizer) { | 81 | private sanitizer: DomSanitizer) { |
69 | } | 82 | } |
@@ -73,21 +86,22 @@ export class DashboardWidgetSelectComponent implements OnInit { | @@ -73,21 +86,22 @@ export class DashboardWidgetSelectComponent implements OnInit { | ||
73 | distinctUntilChanged(), | 86 | distinctUntilChanged(), |
74 | switchMap(search => this.fetchWidgetBundle(search)) | 87 | switchMap(search => this.fetchWidgetBundle(search)) |
75 | ); | 88 | ); |
76 | - this.widgets$ = this.search$.asObservable().pipe( | ||
77 | - distinctUntilChanged(), | ||
78 | - switchMap(search => this.fetchWidget(search)) | 89 | + this.widgets$ = combineLatest([this.search$.asObservable(), this.filterWidgetTypes$.asObservable()]).pipe( |
90 | + distinctUntilChanged((oldValue, newValue) => JSON.stringify(oldValue) === JSON.stringify(newValue)), | ||
91 | + switchMap(search => this.fetchWidget(...search)) | ||
79 | ); | 92 | ); |
80 | } | 93 | } |
81 | 94 | ||
82 | private getWidgets(): Observable<Array<WidgetInfo>> { | 95 | private getWidgets(): Observable<Array<WidgetInfo>> { |
83 | - if (!this.widgetsTypes) { | 96 | + if (!this.widgetsInfo) { |
84 | if (this.widgetsBundle !== null) { | 97 | if (this.widgetsBundle !== null) { |
85 | const bundleAlias = this.widgetsBundle.alias; | 98 | const bundleAlias = this.widgetsBundle.alias; |
86 | const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; | 99 | const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; |
87 | - this.widgetsTypes = this.widgetsService.getBundleWidgetTypes(bundleAlias, isSystem).pipe( | 100 | + this.widgetsInfo = this.widgetsService.getBundleWidgetTypes(bundleAlias, isSystem).pipe( |
88 | map(widgets => widgets.sort((a, b) => b.createdTime - a.createdTime)), | 101 | map(widgets => widgets.sort((a, b) => b.createdTime - a.createdTime)), |
89 | map(widgets => widgets.map((type) => { | 102 | map(widgets => widgets.map((type) => { |
90 | const widgetTypeInfo = toWidgetInfo(type); | 103 | const widgetTypeInfo = toWidgetInfo(type); |
104 | + this.widgetsType.add(widgetTypeInfo.type); | ||
91 | const widget: WidgetInfo = { | 105 | const widget: WidgetInfo = { |
92 | isSystemType: isSystem, | 106 | isSystemType: isSystem, |
93 | bundleAlias, | 107 | bundleAlias, |
@@ -99,14 +113,15 @@ export class DashboardWidgetSelectComponent implements OnInit { | @@ -99,14 +113,15 @@ export class DashboardWidgetSelectComponent implements OnInit { | ||
99 | }; | 113 | }; |
100 | return widget; | 114 | return widget; |
101 | })), | 115 | })), |
116 | + tap(() => this.widgetsTypes.emit(this.widgetsType)), | ||
102 | publishReplay(1), | 117 | publishReplay(1), |
103 | refCount() | 118 | refCount() |
104 | ); | 119 | ); |
105 | } else { | 120 | } else { |
106 | - this.widgetsTypes = of([]); | 121 | + this.widgetsInfo = of([]); |
107 | } | 122 | } |
108 | } | 123 | } |
109 | - return this.widgetsTypes; | 124 | + return this.widgetsInfo; |
110 | } | 125 | } |
111 | 126 | ||
112 | onWidgetClicked($event: Event, widget: WidgetInfo): void { | 127 | onWidgetClicked($event: Event, widget: WidgetInfo): void { |
@@ -149,8 +164,9 @@ export class DashboardWidgetSelectComponent implements OnInit { | @@ -149,8 +164,9 @@ export class DashboardWidgetSelectComponent implements OnInit { | ||
149 | ); | 164 | ); |
150 | } | 165 | } |
151 | 166 | ||
152 | - private fetchWidget(search: string): Observable<Array<WidgetInfo>> { | 167 | + private fetchWidget(search: string, filter: widgetType[]): Observable<Array<WidgetInfo>> { |
153 | return this.getWidgets().pipe( | 168 | return this.getWidgets().pipe( |
169 | + map(widgets => filter ? widgets.filter((widget) => filter.includes(widget.type)) : widgets), | ||
154 | map(widgets => search ? widgets.filter( | 170 | map(widgets => search ? widgets.filter( |
155 | widget => ( | 171 | widget => ( |
156 | widget.title?.toLowerCase().includes(search.toLowerCase()) || | 172 | widget.title?.toLowerCase().includes(search.toLowerCase()) || |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2021 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<div fxLayout="column" class="mat-content mat-padding"> | ||
19 | + <mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="type.display" *ngFor="let type of types" | ||
20 | + (ngModelChange)="update()"> | ||
21 | + {{ 'widget.' + type.type | translate }} | ||
22 | + </mat-checkbox> | ||
23 | +</div> |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +:host { | ||
17 | + width: 100%; | ||
18 | + height: 100%; | ||
19 | + min-width: 200px; | ||
20 | + overflow: hidden; | ||
21 | + background: #fff; | ||
22 | + border-radius: 4px; | ||
23 | + box-shadow: | ||
24 | + 0 7px 8px -4px rgba(0, 0, 0, .2), | ||
25 | + 0 13px 19px 2px rgba(0, 0, 0, .14), | ||
26 | + 0 5px 24px 4px rgba(0, 0, 0, .12); | ||
27 | + | ||
28 | + .mat-content { | ||
29 | + overflow: hidden; | ||
30 | + background-color: #fff; | ||
31 | + } | ||
32 | + | ||
33 | + .mat-padding { | ||
34 | + padding: 16px; | ||
35 | + } | ||
36 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { Component, Inject, InjectionToken } from '@angular/core'; | ||
18 | +import { widgetType } from '@shared/models/widget.models'; | ||
19 | + | ||
20 | +export const DISPLAY_WIDGET_TYPES_PANEL_DATA = new InjectionToken<any>('DisplayWidgetTypesPanelData'); | ||
21 | + | ||
22 | +export interface WidgetTypes { | ||
23 | + type: widgetType; | ||
24 | + display: boolean; | ||
25 | +} | ||
26 | + | ||
27 | +export interface DisplayWidgetTypesPanelData { | ||
28 | + types: WidgetTypes[]; | ||
29 | + typesUpdated: (columns: WidgetTypes[]) => void; | ||
30 | +} | ||
31 | + | ||
32 | +@Component({ | ||
33 | + selector: 'tb-widget-types-panel', | ||
34 | + templateUrl: './widget-types-panel.component.html', | ||
35 | + styleUrls: ['./widget-types-panel.component.scss'] | ||
36 | +}) | ||
37 | +export class DisplayWidgetTypesPanelComponent { | ||
38 | + | ||
39 | + types: WidgetTypes[]; | ||
40 | + | ||
41 | + constructor(@Inject(DISPLAY_WIDGET_TYPES_PANEL_DATA) public data: DisplayWidgetTypesPanelData) { | ||
42 | + this.types = this.data.types; | ||
43 | + } | ||
44 | + | ||
45 | + public update() { | ||
46 | + this.data.typesUpdated(this.types); | ||
47 | + } | ||
48 | +} |
@@ -19,10 +19,8 @@ | @@ -19,10 +19,8 @@ | ||
19 | <mat-toolbar class="details-toolbar" color="primary" [ngStyle]="{height: headerHeightPx+'px'}"> | 19 | <mat-toolbar class="details-toolbar" color="primary" [ngStyle]="{height: headerHeightPx+'px'}"> |
20 | <div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center" style="height: 100%;"> | 20 | <div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center" style="height: 100%;"> |
21 | <div class="mat-toolbar-tools tb-details-title-header" fxFlex fxLayout="column" fxLayoutAlign="start start"> | 21 | <div class="mat-toolbar-tools tb-details-title-header" fxFlex fxLayout="column" fxLayoutAlign="start start"> |
22 | - <div class="tb-details-title" fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center"> | ||
23 | - <button class="tb-mat-28" mat-icon-button type="button" [fxHide]="!showBackStateIcon" (click)="onBackStateDetails()"> | ||
24 | - <mat-icon>arrow_back</mat-icon> | ||
25 | - </button> | 22 | + <div class="tb-details-title" fxLayout="row" fxLayoutAlign="start center"> |
23 | + <ng-content select=".prefix-title-buttons"></ng-content> | ||
26 | <span>{{ headerTitle }}</span> | 24 | <span>{{ headerTitle }}</span> |
27 | </div> | 25 | </div> |
28 | <span class="tb-details-subtitle">{{ headerSubtitle }}</span> | 26 | <span class="tb-details-subtitle">{{ headerSubtitle }}</span> |
@@ -32,7 +32,6 @@ export class DetailsPanelComponent extends PageComponent { | @@ -32,7 +32,6 @@ export class DetailsPanelComponent extends PageComponent { | ||
32 | @Input() headerSubtitle = ''; | 32 | @Input() headerSubtitle = ''; |
33 | @Input() isReadOnly = false; | 33 | @Input() isReadOnly = false; |
34 | @Input() isAlwaysEdit = false; | 34 | @Input() isAlwaysEdit = false; |
35 | - @Input() showBackStateIcon = false; | ||
36 | 35 | ||
37 | theFormValue: FormGroup; | 36 | theFormValue: FormGroup; |
38 | 37 | ||
@@ -51,8 +50,6 @@ export class DetailsPanelComponent extends PageComponent { | @@ -51,8 +50,6 @@ export class DetailsPanelComponent extends PageComponent { | ||
51 | toggleDetailsEditMode = new EventEmitter<boolean>(); | 50 | toggleDetailsEditMode = new EventEmitter<boolean>(); |
52 | @Output() | 51 | @Output() |
53 | applyDetails = new EventEmitter<void>(); | 52 | applyDetails = new EventEmitter<void>(); |
54 | - @Output() | ||
55 | - backStateDetails = new EventEmitter<void>(); | ||
56 | 53 | ||
57 | isEditValue = false; | 54 | isEditValue = false; |
58 | 55 | ||
@@ -78,10 +75,6 @@ export class DetailsPanelComponent extends PageComponent { | @@ -78,10 +75,6 @@ export class DetailsPanelComponent extends PageComponent { | ||
78 | this.closeDetails.emit(); | 75 | this.closeDetails.emit(); |
79 | } | 76 | } |
80 | 77 | ||
81 | - onBackStateDetails() { | ||
82 | - this.backStateDetails.emit(); | ||
83 | - } | ||
84 | - | ||
85 | onToggleDetailsEditMode() { | 78 | onToggleDetailsEditMode() { |
86 | if (!this.isAlwaysEdit) { | 79 | if (!this.isAlwaysEdit) { |
87 | this.isEdit = !this.isEdit; | 80 | this.isEdit = !this.isEdit; |
@@ -132,6 +132,7 @@ import { ManageDashboardStatesDialogComponent } from '@home/components/dashboard | @@ -132,6 +132,7 @@ import { ManageDashboardStatesDialogComponent } from '@home/components/dashboard | ||
132 | import { DashboardStateDialogComponent } from '@home/components/dashboard-page/states/dashboard-state-dialog.component'; | 132 | import { DashboardStateDialogComponent } from '@home/components/dashboard-page/states/dashboard-state-dialog.component'; |
133 | import { EmbedDashboardDialogComponent } from '@home/components/widget/dialog/embed-dashboard-dialog.component'; | 133 | import { EmbedDashboardDialogComponent } from '@home/components/widget/dialog/embed-dashboard-dialog.component'; |
134 | import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/embed-dashboard-dialog-token'; | 134 | import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/embed-dashboard-dialog-token'; |
135 | +import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component'; | ||
135 | 136 | ||
136 | @NgModule({ | 137 | @NgModule({ |
137 | declarations: | 138 | declarations: |
@@ -244,7 +245,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb | @@ -244,7 +245,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb | ||
244 | DashboardSettingsDialogComponent, | 245 | DashboardSettingsDialogComponent, |
245 | ManageDashboardStatesDialogComponent, | 246 | ManageDashboardStatesDialogComponent, |
246 | DashboardStateDialogComponent, | 247 | DashboardStateDialogComponent, |
247 | - EmbedDashboardDialogComponent | 248 | + EmbedDashboardDialogComponent, |
249 | + DisplayWidgetTypesPanelComponent | ||
248 | ], | 250 | ], |
249 | imports: [ | 251 | imports: [ |
250 | CommonModule, | 252 | CommonModule, |
@@ -345,7 +347,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb | @@ -345,7 +347,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb | ||
345 | DashboardSettingsDialogComponent, | 347 | DashboardSettingsDialogComponent, |
346 | ManageDashboardStatesDialogComponent, | 348 | ManageDashboardStatesDialogComponent, |
347 | DashboardStateDialogComponent, | 349 | DashboardStateDialogComponent, |
348 | - EmbedDashboardDialogComponent | 350 | + EmbedDashboardDialogComponent, |
351 | + DisplayWidgetTypesPanelComponent | ||
349 | ], | 352 | ], |
350 | providers: [ | 353 | providers: [ |
351 | WidgetComponentService, | 354 | WidgetComponentService, |
@@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
18 | <div class="input-wrapper" fxLayoutAlign="start center" fxLayoutGap="8px"> | 18 | <div class="input-wrapper" fxLayoutAlign="start center" fxLayoutGap="8px"> |
19 | <mat-icon>search</mat-icon> | 19 | <mat-icon>search</mat-icon> |
20 | <input type="text" [(ngModel)]="searchText" (ngModelChange)="updateSearchText()" [placeholder]=placeholder> | 20 | <input type="text" [(ngModel)]="searchText" (ngModelChange)="updateSearchText()" [placeholder]=placeholder> |
21 | - <button mat-button *ngIf="searchText" mat-icon-button (click)="clear()"> | 21 | + <button mat-button type="button" *ngIf="searchText" mat-icon-button (click)="clear()"> |
22 | <mat-icon>close</mat-icon> | 22 | <mat-icon>close</mat-icon> |
23 | </button> | 23 | </button> |
24 | </div> | 24 | </div> |
@@ -2265,7 +2265,8 @@ | @@ -2265,7 +2265,8 @@ | ||
2265 | "no-data": "No data to display on widget", | 2265 | "no-data": "No data to display on widget", |
2266 | "data-overflow": "Widget displays {{count}} out of {{total}} entities", | 2266 | "data-overflow": "Widget displays {{count}} out of {{total}} entities", |
2267 | "alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities", | 2267 | "alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities", |
2268 | - "search": "Search widget" | 2268 | + "search": "Search widget", |
2269 | + "filter": "Widget filter type" | ||
2269 | }, | 2270 | }, |
2270 | "widget-action": { | 2271 | "widget-action": { |
2271 | "header-button": "Widget header button", | 2272 | "header-button": "Widget header button", |
@@ -753,6 +753,9 @@ mat-label { | @@ -753,6 +753,9 @@ mat-label { | ||
753 | &.tb-mat-20 { | 753 | &.tb-mat-20 { |
754 | @include tb-mat-icon-size(20); | 754 | @include tb-mat-icon-size(20); |
755 | } | 755 | } |
756 | + &.tb-mat-28 { | ||
757 | + @include tb-mat-icon-size(28); | ||
758 | + } | ||
756 | &.tb-mat-32 { | 759 | &.tb-mat-32 { |
757 | @include tb-mat-icon-size(32); | 760 | @include tb-mat-icon-size(32); |
758 | } | 761 | } |