Showing
12 changed files
with
243 additions
and
34 deletions
... | ... | @@ -248,11 +248,14 @@ |
248 | 248 | headerHeightPx="120" |
249 | 249 | [isReadOnly]="true" |
250 | 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 | 257 | <div class="header-pane" *ngIf="isAddingWidget"> |
255 | - <div fxLayout="row"> | |
258 | + <div fxLayout="row" fxLayoutGap="12px"> | |
256 | 259 | <tb-widgets-bundle-search fxFlex |
257 | 260 | [(ngModel)]="searchBundle" |
258 | 261 | placeholder="{{ (!widgetsBundle?.title ? 'widgets-bundle.search' : 'widget.search') | translate }}" |
... | ... | @@ -260,10 +263,21 @@ |
260 | 263 | </tb-widgets-bundle-search> |
261 | 264 | </div> |
262 | 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 | 275 | <tb-dashboard-widget-select *ngIf="isAddingWidget" |
264 | 276 | [aliasController]="dashboardCtx.aliasController" |
265 | 277 | [widgetsBundle]="widgetsBundle" |
266 | 278 | [searchBundle]="searchBundle" |
279 | + [filterWidgetTypes]="filterWidgetTypes" | |
280 | + (widgetsTypes)="updateWidgetsTypes($event)" | |
267 | 281 | (widgetsBundleSelected)="widgetBundleSelected($event)" |
268 | 282 | (widgetSelected)="addWidgetFromType($event)"> |
269 | 283 | </tb-dashboard-widget-select> | ... | ... |
... | ... | @@ -18,11 +18,14 @@ import { |
18 | 18 | ChangeDetectorRef, |
19 | 19 | Component, |
20 | 20 | Inject, |
21 | + Injector, | |
21 | 22 | Input, |
22 | 23 | NgZone, |
23 | 24 | OnDestroy, |
24 | 25 | OnInit, |
26 | + StaticProvider, | |
25 | 27 | ViewChild, |
28 | + ViewContainerRef, | |
26 | 29 | ViewEncapsulation |
27 | 30 | } from '@angular/core'; |
28 | 31 | import { PageComponent } from '@shared/components/page.component'; |
... | ... | @@ -58,7 +61,14 @@ import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; |
58 | 61 | import { MediaBreakpoints } from '@shared/models/constants'; |
59 | 62 | import { AuthUser } from '@shared/models/user.model'; |
60 | 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 | 72 | import { environment as env } from '@env/environment'; |
63 | 73 | import { Authority } from '@shared/models/authority.enum'; |
64 | 74 | import { DialogService } from '@core/services/dialog.service'; |
... | ... | @@ -80,7 +90,10 @@ import { |
80 | 90 | import { EntityAliases } from '@app/shared/models/alias.models'; |
81 | 91 | import { EditWidgetComponent } from '@home/components/dashboard-page/edit-widget.component'; |
82 | 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 | 97 | import { TranslateService } from '@ngx-translate/core'; |
85 | 98 | import { |
86 | 99 | ManageDashboardLayoutsDialogComponent, |
... | ... | @@ -99,6 +112,14 @@ import { ImportExportService } from '@home/components/import-export/import-expor |
99 | 112 | import { AuthState } from '@app/core/auth/auth.models'; |
100 | 113 | import { FiltersDialogComponent, FiltersDialogData } from '@home/components/filter/filters-dialog.component'; |
101 | 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 | 124 | // @dynamic |
104 | 125 | @Component({ |
... | ... | @@ -147,6 +168,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
147 | 168 | isAddingWidgetClosed = true; |
148 | 169 | widgetsBundle: WidgetsBundle = null; |
149 | 170 | searchBundle = ''; |
171 | + widgetTypes: WidgetTypes[] = []; | |
172 | + filterWidgetTypes: widgetType[] = null; | |
150 | 173 | |
151 | 174 | isToolbarOpened = false; |
152 | 175 | isToolbarOpenedAnimate = false; |
... | ... | @@ -261,6 +284,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
261 | 284 | private dialog: MatDialog, |
262 | 285 | private translate: TranslateService, |
263 | 286 | private ngZone: NgZone, |
287 | + private overlay: Overlay, | |
288 | + private viewContainerRef: ViewContainerRef, | |
264 | 289 | private cd: ChangeDetectorRef) { |
265 | 290 | super(store); |
266 | 291 | |
... | ... | @@ -1121,6 +1146,55 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
1121 | 1146 | |
1122 | 1147 | widgetBundleSelected(bundle: WidgetsBundle){ |
1123 | 1148 | this.widgetsBundle = bundle; |
1149 | + this.widgetTypes = []; | |
1124 | 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 | 19 | import { IAliasController } from '@core/api/widget-api.models'; |
20 | 20 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
21 | 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 | 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 | 26 | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
27 | 27 | import { isDefinedAndNotNull } from '@core/utils'; |
28 | 28 | |
... | ... | @@ -34,15 +34,20 @@ import { isDefinedAndNotNull } from '@core/utils'; |
34 | 34 | export class DashboardWidgetSelectComponent implements OnInit { |
35 | 35 | |
36 | 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 | 39 | private widgetsBundleValue: WidgetsBundle; |
40 | + private widgetsType = new Set<widgetType>(); | |
39 | 41 | |
40 | 42 | widgets$: Observable<Array<WidgetInfo>>; |
41 | 43 | widgetsBundles$: Observable<Array<WidgetsBundle>>; |
42 | 44 | |
43 | 45 | @Input() |
44 | 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 | 51 | this.widgetsBundleValue = widgetBundle; |
47 | 52 | } |
48 | 53 | |
... | ... | @@ -58,12 +63,20 @@ export class DashboardWidgetSelectComponent implements OnInit { |
58 | 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 | 71 | @Output() |
62 | 72 | widgetSelected: EventEmitter<WidgetInfo> = new EventEmitter<WidgetInfo>(); |
63 | 73 | |
64 | 74 | @Output() |
65 | 75 | widgetsBundleSelected: EventEmitter<WidgetsBundle> = new EventEmitter<WidgetsBundle>(); |
66 | 76 | |
77 | + @Output() | |
78 | + widgetsTypes: EventEmitter<Set<widgetType>> = new EventEmitter<Set<widgetType>>(); | |
79 | + | |
67 | 80 | constructor(private widgetsService: WidgetService, |
68 | 81 | private sanitizer: DomSanitizer) { |
69 | 82 | } |
... | ... | @@ -73,21 +86,22 @@ export class DashboardWidgetSelectComponent implements OnInit { |
73 | 86 | distinctUntilChanged(), |
74 | 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 | 95 | private getWidgets(): Observable<Array<WidgetInfo>> { |
83 | - if (!this.widgetsTypes) { | |
96 | + if (!this.widgetsInfo) { | |
84 | 97 | if (this.widgetsBundle !== null) { |
85 | 98 | const bundleAlias = this.widgetsBundle.alias; |
86 | 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 | 101 | map(widgets => widgets.sort((a, b) => b.createdTime - a.createdTime)), |
89 | 102 | map(widgets => widgets.map((type) => { |
90 | 103 | const widgetTypeInfo = toWidgetInfo(type); |
104 | + this.widgetsType.add(widgetTypeInfo.type); | |
91 | 105 | const widget: WidgetInfo = { |
92 | 106 | isSystemType: isSystem, |
93 | 107 | bundleAlias, |
... | ... | @@ -99,14 +113,15 @@ export class DashboardWidgetSelectComponent implements OnInit { |
99 | 113 | }; |
100 | 114 | return widget; |
101 | 115 | })), |
116 | + tap(() => this.widgetsTypes.emit(this.widgetsType)), | |
102 | 117 | publishReplay(1), |
103 | 118 | refCount() |
104 | 119 | ); |
105 | 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 | 127 | onWidgetClicked($event: Event, widget: WidgetInfo): void { |
... | ... | @@ -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 | 168 | return this.getWidgets().pipe( |
169 | + map(widgets => filter ? widgets.filter((widget) => filter.includes(widget.type)) : widgets), | |
154 | 170 | map(widgets => search ? widgets.filter( |
155 | 171 | widget => ( |
156 | 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 | 19 | <mat-toolbar class="details-toolbar" color="primary" [ngStyle]="{height: headerHeightPx+'px'}"> |
20 | 20 | <div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center" style="height: 100%;"> |
21 | 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 | 24 | <span>{{ headerTitle }}</span> |
27 | 25 | </div> |
28 | 26 | <span class="tb-details-subtitle">{{ headerSubtitle }}</span> | ... | ... |
... | ... | @@ -32,7 +32,6 @@ export class DetailsPanelComponent extends PageComponent { |
32 | 32 | @Input() headerSubtitle = ''; |
33 | 33 | @Input() isReadOnly = false; |
34 | 34 | @Input() isAlwaysEdit = false; |
35 | - @Input() showBackStateIcon = false; | |
36 | 35 | |
37 | 36 | theFormValue: FormGroup; |
38 | 37 | |
... | ... | @@ -51,8 +50,6 @@ export class DetailsPanelComponent extends PageComponent { |
51 | 50 | toggleDetailsEditMode = new EventEmitter<boolean>(); |
52 | 51 | @Output() |
53 | 52 | applyDetails = new EventEmitter<void>(); |
54 | - @Output() | |
55 | - backStateDetails = new EventEmitter<void>(); | |
56 | 53 | |
57 | 54 | isEditValue = false; |
58 | 55 | |
... | ... | @@ -78,10 +75,6 @@ export class DetailsPanelComponent extends PageComponent { |
78 | 75 | this.closeDetails.emit(); |
79 | 76 | } |
80 | 77 | |
81 | - onBackStateDetails() { | |
82 | - this.backStateDetails.emit(); | |
83 | - } | |
84 | - | |
85 | 78 | onToggleDetailsEditMode() { |
86 | 79 | if (!this.isAlwaysEdit) { |
87 | 80 | this.isEdit = !this.isEdit; | ... | ... |
... | ... | @@ -132,6 +132,7 @@ import { ManageDashboardStatesDialogComponent } from '@home/components/dashboard |
132 | 132 | import { DashboardStateDialogComponent } from '@home/components/dashboard-page/states/dashboard-state-dialog.component'; |
133 | 133 | import { EmbedDashboardDialogComponent } from '@home/components/widget/dialog/embed-dashboard-dialog.component'; |
134 | 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 | 137 | @NgModule({ |
137 | 138 | declarations: |
... | ... | @@ -244,7 +245,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb |
244 | 245 | DashboardSettingsDialogComponent, |
245 | 246 | ManageDashboardStatesDialogComponent, |
246 | 247 | DashboardStateDialogComponent, |
247 | - EmbedDashboardDialogComponent | |
248 | + EmbedDashboardDialogComponent, | |
249 | + DisplayWidgetTypesPanelComponent | |
248 | 250 | ], |
249 | 251 | imports: [ |
250 | 252 | CommonModule, |
... | ... | @@ -345,7 +347,8 @@ import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/emb |
345 | 347 | DashboardSettingsDialogComponent, |
346 | 348 | ManageDashboardStatesDialogComponent, |
347 | 349 | DashboardStateDialogComponent, |
348 | - EmbedDashboardDialogComponent | |
350 | + EmbedDashboardDialogComponent, | |
351 | + DisplayWidgetTypesPanelComponent | |
349 | 352 | ], |
350 | 353 | providers: [ |
351 | 354 | WidgetComponentService, | ... | ... |
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | <div class="input-wrapper" fxLayoutAlign="start center" fxLayoutGap="8px"> |
19 | 19 | <mat-icon>search</mat-icon> |
20 | 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 | 22 | <mat-icon>close</mat-icon> |
23 | 23 | </button> |
24 | 24 | </div> | ... | ... |
... | ... | @@ -2265,7 +2265,8 @@ |
2265 | 2265 | "no-data": "No data to display on widget", |
2266 | 2266 | "data-overflow": "Widget displays {{count}} out of {{total}} entities", |
2267 | 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 | 2271 | "widget-action": { |
2271 | 2272 | "header-button": "Widget header button", | ... | ... |