Commit f579add9a777237acb1fb7b94e11f99838000ab0

Authored by Igor Kulikov
1 parent b3df9644

Layout improvements.

@@ -184,6 +184,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo @@ -184,6 +184,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
184 swap: false, 184 swap: false,
185 maxRows: 100, 185 maxRows: 100,
186 minCols: this.columns ? this.columns : 24, 186 minCols: this.columns ? this.columns : 24,
  187 + maxCols: 3000,
  188 + maxItemCols: 1000,
  189 + maxItemRows: 1000,
  190 + maxItemArea: 1000000,
187 outerMargin: true, 191 outerMargin: true,
188 margin: this.margin ? this.margin : 10, 192 margin: this.margin ? this.margin : 10,
189 minItemCols: 1, 193 minItemCols: 1,
@@ -299,14 +299,16 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni @@ -299,14 +299,16 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni
299 299
300 writeValue(obj: WidgetActionsData): void { 300 writeValue(obj: WidgetActionsData): void {
301 this.innerValue = obj; 301 this.innerValue = obj;
302 - setTimeout(() => {  
303 - this.dataSource.setActions(this.innerValue);  
304 - if (this.viewsInited) {  
305 - this.resetSortAndFilter(true);  
306 - } else {  
307 - this.dirtyValue = true;  
308 - }  
309 - }, 0); 302 + if (this.innerValue) {
  303 + setTimeout(() => {
  304 + this.dataSource.setActions(this.innerValue);
  305 + if (this.viewsInited) {
  306 + this.resetSortAndFilter(true);
  307 + } else {
  308 + this.dirtyValue = true;
  309 + }
  310 + }, 0);
  311 + }
310 } 312 }
311 313
312 private onActionsUpdated() { 314 private onActionsUpdated() {
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 <div class="tb-color-preview" (click)="showColorPicker(key)" style="margin-right: 5px;"> 29 <div class="tb-color-preview" (click)="showColorPicker(key)" style="margin-right: 5px;">
30 <div class="tb-color-result" [ngStyle]="{background: key.color}"></div> 30 <div class="tb-color-result" [ngStyle]="{background: key.color}"></div>
31 </div> 31 </div>
32 - <div style="flex: 1; min-width: 0px;" fxLayout="row"> 32 + <div class="tb-chip-labels">
33 <div class="tb-chip-label"> 33 <div class="tb-chip-label">
34 <span *ngIf="datasourceType !== datasourceTypes.function && widgetType !== widgetTypes.alarm"> 34 <span *ngIf="datasourceType !== datasourceTypes.function && widgetType !== widgetTypes.alarm">
35 <span *ngIf="key.type === dataKeyTypes.attribute" 35 <span *ngIf="key.type === dataKeyTypes.attribute"
@@ -30,14 +30,19 @@ @@ -30,14 +30,19 @@
30 } 30 }
31 } 31 }
32 32
33 - .tb-chip-label {  
34 - overflow: hidden;  
35 - text-overflow: ellipsis;  
36 - white-space: nowrap;  
37 - } 33 + .tb-chip-labels {
  34 + display: flex;
  35 + flex-direction: row;
  36 + min-width: 0px;
  37 + .tb-chip-label {
  38 + overflow: hidden;
  39 + text-overflow: ellipsis;
  40 + white-space: nowrap;
  41 + }
38 42
39 - .tb-chip-separator {  
40 - white-space: pre; 43 + .tb-chip-separator {
  44 + white-space: pre;
  45 + }
41 } 46 }
42 47
43 .mat-chip-remove.mat-icon { 48 .mat-chip-remove.mat-icon {
@@ -38,16 +38,16 @@ @@ -38,16 +38,16 @@
38 </mat-option> 38 </mat-option>
39 </mat-select> 39 </mat-select>
40 </mat-form-field> 40 </mat-form-field>
41 - <mat-checkbox fxFlex formControlName="showMin"> 41 + <mat-checkbox formControlName="showMin">
42 {{ 'legend.show-min' | translate }} 42 {{ 'legend.show-min' | translate }}
43 </mat-checkbox> 43 </mat-checkbox>
44 - <mat-checkbox fxFlex formControlName="showMax"> 44 + <mat-checkbox formControlName="showMax">
45 {{ 'legend.show-max' | translate }} 45 {{ 'legend.show-max' | translate }}
46 </mat-checkbox> 46 </mat-checkbox>
47 - <mat-checkbox fxFlex formControlName="showAvg"> 47 + <mat-checkbox formControlName="showAvg">
48 {{ 'legend.show-avg' | translate }} 48 {{ 'legend.show-avg' | translate }}
49 </mat-checkbox> 49 </mat-checkbox>
50 - <mat-checkbox fxFlex formControlName="showTotal"> 50 + <mat-checkbox formControlName="showTotal">
51 {{ 'legend.show-total' | translate }} 51 {{ 'legend.show-total' | translate }}
52 </mat-checkbox> 52 </mat-checkbox>
53 </section> 53 </section>
@@ -22,11 +22,10 @@ @@ -22,11 +22,10 @@
22 <div *ngIf="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.alarm" 22 <div *ngIf="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.alarm"
23 fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center"> 23 fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center">
24 <div fxLayout="column" fxLayoutGap="8px" fxFlex> 24 <div fxLayout="column" fxLayoutGap="8px" fxFlex>
25 - <mat-checkbox fxFlex formControlName="useDashboardTimewindow"> 25 + <mat-checkbox formControlName="useDashboardTimewindow">
26 {{ 'widget-config.use-dashboard-timewindow' | translate }} 26 {{ 'widget-config.use-dashboard-timewindow' | translate }}
27 </mat-checkbox> 27 </mat-checkbox>
28 - <mat-checkbox fxFlex  
29 - formControlName="displayTimewindow"> 28 + <mat-checkbox formControlName="displayTimewindow">
30 {{ 'widget-config.display-timewindow' | translate }} 29 {{ 'widget-config.display-timewindow' | translate }}
31 </mat-checkbox> 30 </mat-checkbox>
32 </div> 31 </div>
@@ -26,6 +26,9 @@ @@ -26,6 +26,9 @@
26 } 26 }
27 .tb-datasource-type { 27 .tb-datasource-type {
28 min-width: 110px; 28 min-width: 110px;
  29 + @media #{$mat-gt-sm} {
  30 + max-width: 110px;
  31 + }
29 } 32 }
30 .tb-datasource { 33 .tb-datasource {
31 @media #{$mat-gt-sm} { 34 @media #{$mat-gt-sm} {
@@ -163,6 +163,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont @@ -163,6 +163,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
163 } else { 163 } else {
164 this.datasourceTypes = [DatasourceType.function, DatasourceType.entity]; 164 this.datasourceTypes = [DatasourceType.function, DatasourceType.entity];
165 } 165 }
  166 + this.dataSettings = this.fb.group({});
  167 + this.targetDeviceSettings = this.fb.group({});
  168 + this.alarmSourceSettings = this.fb.group({});
  169 + this.advancedSettings = this.fb.group({});
166 this.widgetSettings = this.fb.group({ 170 this.widgetSettings = this.fb.group({
167 title: [null, []], 171 title: [null, []],
168 showTitleIcon: [null, []], 172 showTitleIcon: [null, []],
@@ -574,7 +578,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont @@ -574,7 +578,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
574 } 578 }
575 579
576 public displayAdvanced(): boolean { 580 public displayAdvanced(): boolean {
577 - return this.modelValue.settingsSchema && this.modelValue.settingsSchema.schema; 581 + return this.modelValue && this.modelValue.settingsSchema && this.modelValue.settingsSchema.schema;
578 } 582 }
579 583
580 public removeDatasource(index: number) { 584 public removeDatasource(index: number) {
@@ -772,7 +776,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont @@ -772,7 +776,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
772 valid: false 776 valid: false
773 } 777 }
774 }; 778 };
775 - } else { 779 + } else if (this.modelValue) {
776 const config = this.modelValue.config; 780 const config = this.modelValue.config;
777 if (this.widgetType === widgetType.rpc && this.modelValue.isDataEnabled) { 781 if (this.widgetType === widgetType.rpc && this.modelValue.isDataEnabled) {
778 if (!config.targetDeviceAliasIds || !config.targetDeviceAliasIds.length) { 782 if (!config.targetDeviceAliasIds || !config.targetDeviceAliasIds.length) {
@@ -120,59 +120,90 @@ @@ -120,59 +120,90 @@
120 'tb-dashboard-toolbar-opened': toolbarOpened, 120 'tb-dashboard-toolbar-opened': toolbarOpened,
121 'tb-dashboard-toolbar-animated': isToolbarOpenedAnimate, 121 'tb-dashboard-toolbar-animated': isToolbarOpenedAnimate,
122 'tb-dashboard-toolbar-closed': !toolbarOpened }"> 122 'tb-dashboard-toolbar-closed': !toolbarOpened }">
123 - <section *ngIf="!widgetEditMode" class="tb-dashboard-title" fxLayout="row" fxLayoutAlign="center center"  
124 - [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">  
125 - <h3 [fxShow]="!isEdit && displayTitle()">{{ dashboard.title }}</h3>  
126 - <mat-form-field [fxShow]="isEdit" class="mat-block" style="height: 30px;">  
127 - <mat-label translate [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">dashboard.title</mat-label>  
128 - <input matInput class="tb-dashboard-title"  
129 - [ngStyle]="{'color': dashboard.configuration.settings.titleColor}"  
130 - required name="title" [(ngModel)]="dashboard.title">  
131 - </mat-form-field>  
132 - </section>  
133 - <div class="tb-absolute-fill tb-dashboard-layouts" fxLayout="{{forceDashboardMobileMode ? 'column' : 'row'}}"  
134 - [ngClass]="{ 'tb-padded' : !widgetEditMode && (isEdit || displayTitle()), 'tb-shrinked' : isEditingWidget }">  
135 - <mat-drawer-container class="tb-absolute-fill">  
136 - <mat-drawer *ngIf="layouts.right.show"  
137 - id="tb-right-layout"  
138 - [ngStyle]="{minWidth: rightLayoutWidth(),  
139 - maxWidth: rightLayoutWidth(),  
140 - height: rightLayoutHeight(),  
141 - borderLeft: 'none'}"  
142 - disableClose="true"  
143 - position="end"  
144 - [mode]="isMobile ? 'over' : 'side'"  
145 - [(opened)]="rightLayoutOpened">  
146 - <tb-dashboard-layout style="height: 100%;"  
147 - [layoutCtx]="layouts.right.layoutCtx"  
148 - [dashboardCtx]="dashboardCtx"  
149 - [isEdit]="isEdit"  
150 - [isEditingWidget]="isEditingWidget"  
151 - [isMobile]="forceDashboardMobileMode"  
152 - [widgetEditMode]="widgetEditMode">  
153 - </tb-dashboard-layout>  
154 - </mat-drawer>  
155 - <mat-drawer-content [fxShow]="layouts.main.show"  
156 - id="tb-main-layout"  
157 - [ngStyle]="{width: mainLayoutWidth(),  
158 - height: mainLayoutHeight()}">  
159 - <tb-dashboard-layout  
160 - [layoutCtx]="layouts.main.layoutCtx"  
161 - [dashboardCtx]="dashboardCtx"  
162 - [isEdit]="isEdit"  
163 - [isEditingWidget]="isEditingWidget"  
164 - [isMobile]="forceDashboardMobileMode"  
165 - [widgetEditMode]="widgetEditMode">  
166 - </tb-dashboard-layout>  
167 - </mat-drawer-content>  
168 - </mat-drawer-container>  
169 - </div>  
170 - <mat-drawer-container hasBackdrop="false" class="tb-widget-details-sidenav"> 123 + <mat-drawer-container hasBackdrop="false" class="tb-absolute-fill tb-dashboard-drawer-container">
  124 + <mat-drawer-content fxLayout="column" fxLayoutAlign="center start">
  125 + <section *ngIf="!widgetEditMode" class="tb-dashboard-title"
  126 + [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">
  127 + <h3 [fxShow]="!isEdit && displayTitle()">{{ dashboard.title }}</h3>
  128 + <mat-form-field [fxShow]="isEdit" class="mat-block">
  129 + <mat-label translate [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">dashboard.title</mat-label>
  130 + <input matInput class="tb-dashboard-title"
  131 + [ngStyle]="{'color': dashboard.configuration.settings.titleColor}"
  132 + required name="title" [(ngModel)]="dashboard.title">
  133 + </mat-form-field>
  134 + </section>
  135 + <mat-drawer-container class="tb-dashboard-layouts" fxFlex
  136 + [ngClass]="{ 'tb-shrinked' : isEditingWidget }">
  137 + <mat-drawer *ngIf="layouts.right.show"
  138 + id="tb-right-layout"
  139 + [ngStyle]="{minWidth: rightLayoutWidth(),
  140 + maxWidth: rightLayoutWidth(),
  141 + height: rightLayoutHeight(),
  142 + borderLeft: 'none'}"
  143 + disableClose="true"
  144 + position="end"
  145 + [mode]="isMobile ? 'over' : 'side'"
  146 + [(opened)]="rightLayoutOpened">
  147 + <tb-dashboard-layout style="height: 100%;"
  148 + [layoutCtx]="layouts.right.layoutCtx"
  149 + [dashboardCtx]="dashboardCtx"
  150 + [isEdit]="isEdit"
  151 + [isEditingWidget]="isEditingWidget"
  152 + [isMobile]="forceDashboardMobileMode"
  153 + [widgetEditMode]="widgetEditMode">
  154 + </tb-dashboard-layout>
  155 + </mat-drawer>
  156 + <mat-drawer-content [fxShow]="layouts.main.show"
  157 + id="tb-main-layout"
  158 + [ngStyle]="{width: mainLayoutWidth(),
  159 + height: mainLayoutHeight()}">
  160 + <tb-dashboard-layout
  161 + [layoutCtx]="layouts.main.layoutCtx"
  162 + [dashboardCtx]="dashboardCtx"
  163 + [isEdit]="isEdit"
  164 + [isEditingWidget]="isEditingWidget"
  165 + [isMobile]="forceDashboardMobileMode"
  166 + [widgetEditMode]="widgetEditMode">
  167 + </tb-dashboard-layout>
  168 + </mat-drawer-content>
  169 + </mat-drawer-container>
  170 + <section fxLayout="row" class="layout-wrap tb-footer-buttons" fxLayoutAlign="start end">
  171 + <tb-footer-fab-buttons [fxShow]="!isAddingWidget && isEdit && !widgetEditMode"
  172 + relative
  173 + [footerFabButtons]="addWidgetFabButtons">
  174 + </tb-footer-fab-buttons>
  175 + <button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen"
  176 + mat-fab color="accent" class="tb-btn-footer"
  177 + [ngClass]="{'tb-hide': !isEdit || isAddingWidget}"
  178 + [disabled]="isLoading$ | async"
  179 + (click)="saveDashboard()"
  180 + matTooltip="{{ 'action.apply-changes' | translate }}"
  181 + matTooltipPosition="above">
  182 + <mat-icon>done</mat-icon>
  183 + </button>
  184 + <button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen"
  185 + mat-fab color="accent" class="tb-btn-footer"
  186 + [ngClass]="{'tb-hide': isAddingWidget || (isLoading$ | async)}"
  187 + [disabled]="isLoading$ | async"
  188 + (click)="toggleDashboardEditMode()"
  189 + matTooltip="{{ (isEdit ? 'action.decline-changes': 'action.enter-edit-mode') | translate }}"
  190 + matTooltipPosition="above">
  191 + <mat-icon>{{ isEdit ? 'close' : 'edit' }}</mat-icon>
  192 + </button>
  193 + </section>
  194 + <section class="tb-powered-by-footer" [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">
  195 + <span>Powered by <a href="https://thingsboard.io" target="_blank">Thingsboard v.{{ thingsboardVersion }}</a></span>
  196 + </section>
  197 + </mat-drawer-content>
171 <mat-drawer class="tb-details-drawer" 198 <mat-drawer class="tb-details-drawer"
172 - [opened]="isEditingWidget" 199 + [opened]="isEditingWidget || isAddingWidget"
  200 + (openedStart)="detailsDrawerOpenedStart()"
  201 + (opened)="detailsDrawerOpened()"
  202 + (closedStart)="detailsDrawerClosedStart()"
  203 + (closed)="detailsDrawerClosed()"
173 mode="over" 204 mode="over"
174 position="end"> 205 position="end">
175 - <tb-details-panel *ngIf="isEditingWidget" fxFlex 206 + <tb-details-panel *ngIf="!isEditingWidgetClosed" fxFlex
176 headerTitle="{{editingWidget?.config.title}}" 207 headerTitle="{{editingWidget?.config.title}}"
177 headerSubtitle="{{ editingWidgetSubtitle }}" 208 headerSubtitle="{{ editingWidgetSubtitle }}"
178 [isReadOnly]="false" 209 [isReadOnly]="false"
@@ -185,21 +216,14 @@ @@ -185,21 +216,14 @@
185 <div [tb-help]="helpLinkIdForWidgetType()"></div> 216 <div [tb-help]="helpLinkIdForWidgetType()"></div>
186 </div> 217 </div>
187 <tb-edit-widget #tbEditWidget 218 <tb-edit-widget #tbEditWidget
188 - [dashboard]="dashboard"  
189 - [aliasController]="dashboardCtx.aliasController"  
190 - [widgetEditMode]="widgetEditMode"  
191 - [widget]="editingWidget"  
192 - [widgetLayout]="editingWidgetLayout"> 219 + [dashboard]="dashboard"
  220 + [aliasController]="dashboardCtx.aliasController"
  221 + [widgetEditMode]="widgetEditMode"
  222 + [widget]="editingWidget"
  223 + [widgetLayout]="editingWidgetLayout">
193 </tb-edit-widget> 224 </tb-edit-widget>
194 </tb-details-panel> 225 </tb-details-panel>
195 - </mat-drawer>  
196 - </mat-drawer-container>  
197 - <mat-drawer-container *ngIf="!widgetEditMode" hasBackdrop="false" class="tb-select-widget-sidenav">  
198 - <mat-drawer class="tb-details-drawer"  
199 - [opened]="isAddingWidget"  
200 - mode="over"  
201 - position="end">  
202 - <tb-details-panel *ngIf="isAddingWidget" fxFlex 226 + <tb-details-panel *ngIf="!isAddingWidgetClosed && !widgetEditMode" fxFlex
203 headerTitle="{{'dashboard.select-widget-title' | translate}}" 227 headerTitle="{{'dashboard.select-widget-title' | translate}}"
204 headerHeightPx="120" 228 headerHeightPx="120"
205 [isReadOnly]="true" 229 [isReadOnly]="true"
@@ -218,40 +242,12 @@ @@ -218,40 +242,12 @@
218 </div> 242 </div>
219 </div> 243 </div>
220 <tb-dashboard-widget-select *ngIf="isAddingWidget" 244 <tb-dashboard-widget-select *ngIf="isAddingWidget"
221 - [aliasController]="dashboardCtx.aliasController"  
222 - [widgetsBundle]="widgetsBundle"  
223 - (widgetSelected)="addWidgetFromType($event)"> 245 + [aliasController]="dashboardCtx.aliasController"
  246 + [widgetsBundle]="widgetsBundle"
  247 + (widgetSelected)="addWidgetFromType($event)">
224 </tb-dashboard-widget-select> 248 </tb-dashboard-widget-select>
225 </tb-details-panel> 249 </tb-details-panel>
226 </mat-drawer> 250 </mat-drawer>
227 </mat-drawer-container> 251 </mat-drawer-container>
228 - <!--tb-details-sidenav TODO -->  
229 - <section fxLayout="row" class="layout-wrap tb-footer-buttons" fxLayoutAlign="start end">  
230 - <tb-footer-fab-buttons [fxShow]="!isAddingWidget && isEdit && !widgetEditMode"  
231 - relative  
232 - [footerFabButtons]="addWidgetFabButtons">  
233 - </tb-footer-fab-buttons>  
234 - <button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen"  
235 - mat-fab color="accent" class="tb-btn-footer"  
236 - [ngClass]="{'tb-hide': !isEdit || isAddingWidget}"  
237 - [disabled]="isLoading$ | async"  
238 - (click)="saveDashboard()"  
239 - matTooltip="{{ 'action.apply-changes' | translate }}"  
240 - matTooltipPosition="above">  
241 - <mat-icon>done</mat-icon>  
242 - </button>  
243 - <button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen"  
244 - mat-fab color="accent" class="tb-btn-footer"  
245 - [ngClass]="{'tb-hide': isAddingWidget || (isLoading$ | async)}"  
246 - [disabled]="isLoading$ | async"  
247 - (click)="toggleDashboardEditMode()"  
248 - matTooltip="{{ (isEdit ? 'action.decline-changes': 'action.enter-edit-mode') | translate }}"  
249 - matTooltipPosition="above">  
250 - <mat-icon>{{ isEdit ? 'close' : 'edit' }}</mat-icon>  
251 - </button>  
252 - </section>  
253 - </section>  
254 - <section class="tb-powered-by-footer" [ngStyle]="{'color': dashboard.configuration.settings.titleColor}">  
255 - <span>Powered by <a href="https://thingsboard.io" target="_blank">Thingsboard v.{{ thingsboardVersion }}</a></span>  
256 </section> 252 </section>
257 </div> 253 </div>
@@ -32,9 +32,9 @@ div.tb-dashboard-page { @@ -32,9 +32,9 @@ div.tb-dashboard-page {
32 background-color: #eee; 32 background-color: #eee;
33 } 33 }
34 section.tb-dashboard-title { 34 section.tb-dashboard-title {
35 - position: absolute;  
36 - top: 0;  
37 - left: 20px; 35 + position: relative;
  36 + padding-left: 20px;
  37 + max-height: 60px;
38 mat-form-field { 38 mat-form-field {
39 .mat-form-field-infix { 39 .mat-form-field-infix {
40 width: 100%; 40 width: 100%;
@@ -47,17 +47,6 @@ div.tb-dashboard-page { @@ -47,17 +47,6 @@ div.tb-dashboard-page {
47 letter-spacing: .005em; 47 letter-spacing: .005em;
48 } 48 }
49 } 49 }
50 - div.tb-padded {  
51 - top: 60px;  
52 - }  
53 -  
54 - section.tb-padded {  
55 - top: 60px;  
56 - }  
57 -  
58 - div.tb-shrinked {  
59 - width: 40%;  
60 - }  
61 50
62 section.tb-dashboard-toolbar { 51 section.tb-dashboard-toolbar {
63 position: absolute; 52 position: absolute;
@@ -106,21 +95,16 @@ div.tb-dashboard-page { @@ -106,21 +95,16 @@ div.tb-dashboard-page {
106 transition: margin-top .3s cubic-bezier(.55, 0, .55, .2) .2s; 95 transition: margin-top .3s cubic-bezier(.55, 0, .55, .2) .2s;
107 } 96 }
108 } 97 }
  98 + }
109 99
110 - .tb-dashboard-layouts {  
111 - /*md-backdrop {  
112 - z-index: 1;  
113 - }*/  
114 - #tb-right-layout {  
115 - mat-drawer {  
116 - z-index: 1;  
117 - } 100 + mat-drawer-container.tb-dashboard-drawer-container {
  101 + mat-drawer-container.tb-dashboard-layouts {
  102 + width: 100%;
  103 + &.tb-shrinked {
  104 + width: 40%;
118 } 105 }
119 } 106 }
120 - }  
121 107
122 - mat-drawer-container.tb-widget-details-sidenav {  
123 - position: initial;  
124 mat-drawer.tb-details-drawer { 108 mat-drawer.tb-details-drawer {
125 @media #{$mat-gt-sm} { 109 @media #{$mat-gt-sm} {
126 width: 85% !important; 110 width: 85% !important;
@@ -136,10 +120,6 @@ div.tb-dashboard-page { @@ -136,10 +120,6 @@ div.tb-dashboard-page {
136 } 120 }
137 } 121 }
138 122
139 - mat-drawer-container.tb-select-widget-sidenav {  
140 - position: initial;  
141 - }  
142 -  
143 section.tb-powered-by-footer { 123 section.tb-powered-by-footer {
144 position: absolute; 124 position: absolute;
145 right: 25px; 125 right: 25px;
@@ -104,9 +104,11 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -104,9 +104,11 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
104 isFullscreen = false; 104 isFullscreen = false;
105 isEdit = false; 105 isEdit = false;
106 isEditingWidget = false; 106 isEditingWidget = false;
  107 + isEditingWidgetClosed = true;
107 isMobile = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']); 108 isMobile = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']);
108 forceDashboardMobileMode = false; 109 forceDashboardMobileMode = false;
109 isAddingWidget = false; 110 isAddingWidget = false;
  111 + isAddingWidgetClosed = true;
110 widgetsBundle: WidgetsBundle = null; 112 widgetsBundle: WidgetsBundle = null;
111 113
112 isToolbarOpened = false; 114 isToolbarOpened = false;
@@ -284,8 +286,10 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -284,8 +286,10 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
284 this.isFullscreen = false; 286 this.isFullscreen = false;
285 this.isEdit = false; 287 this.isEdit = false;
286 this.isEditingWidget = false; 288 this.isEditingWidget = false;
  289 + this.isEditingWidgetClosed = true;
287 this.forceDashboardMobileMode = false; 290 this.forceDashboardMobileMode = false;
288 this.isAddingWidget = false; 291 this.isAddingWidget = false;
  292 + this.isAddingWidgetClosed = true;
289 this.widgetsBundle = null; 293 this.widgetsBundle = null;
290 294
291 this.isToolbarOpened = false; 295 this.isToolbarOpened = false;
@@ -694,6 +698,31 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -694,6 +698,31 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
694 this.isAddingWidget = false; 698 this.isAddingWidget = false;
695 } 699 }
696 700
  701 + detailsDrawerOpenedStart() {
  702 + if (this.isEditingWidget) {
  703 + this.isEditingWidgetClosed = false;
  704 + } else if (this.isAddingWidget) {
  705 + this.isAddingWidgetClosed = false;
  706 + }
  707 + setTimeout(() => {
  708 + this.cd.detach();
  709 + }, 0);
  710 + }
  711 +
  712 + detailsDrawerOpened() {
  713 + this.cd.reattach();
  714 + }
  715 +
  716 + detailsDrawerClosedStart() {
  717 + this.cd.detach();
  718 + }
  719 +
  720 + detailsDrawerClosed() {
  721 + this.isEditingWidgetClosed = true;
  722 + this.isAddingWidgetClosed = true;
  723 + this.cd.reattach();
  724 + }
  725 +
697 private addWidgetToLayout(widget: Widget, layoutId: DashboardLayoutId) { 726 private addWidgetToLayout(widget: Widget, layoutId: DashboardLayoutId) {
698 this.dashboardUtils.addWidgetToLayout(this.dashboard, this.dashboardCtx.state, layoutId, widget); 727 this.dashboardUtils.addWidgetToLayout(this.dashboard, this.dashboardCtx.state, layoutId, widget);
699 this.layouts[layoutId].layoutCtx.widgets.addWidgetId(widget.id); 728 this.layouts[layoutId].layoutCtx.widgets.addWidgetId(widget.id);
@@ -77,6 +77,8 @@ export class LayoutWidgetsArray implements Iterable<Widget> { @@ -77,6 +77,8 @@ export class LayoutWidgetsArray implements Iterable<Widget> {
77 private widgetIds: string[] = []; 77 private widgetIds: string[] = [];
78 private pointer = 0; 78 private pointer = 0;
79 79
  80 + private loaded = false;
  81 +
80 constructor(private dashboard: Dashboard) { 82 constructor(private dashboard: Dashboard) {
81 } 83 }
82 84
@@ -84,8 +86,17 @@ export class LayoutWidgetsArray implements Iterable<Widget> { @@ -84,8 +86,17 @@ export class LayoutWidgetsArray implements Iterable<Widget> {
84 return this.widgetIds.length; 86 return this.widgetIds.length;
85 } 87 }
86 88
  89 + isLoading() {
  90 + return !this.loaded;
  91 + }
  92 +
  93 + isEmpty() {
  94 + return this.loaded && this.widgetIds.length === 0;
  95 + }
  96 +
87 setWidgetIds(widgetIds: string[]) { 97 setWidgetIds(widgetIds: string[]) {
88 this.widgetIds = widgetIds; 98 this.widgetIds = widgetIds;
  99 + this.loaded = true;
89 } 100 }
90 101
91 addWidgetId(widgetId: string) { 102 addWidgetId(widgetId: string) {
@@ -102,6 +102,9 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan @@ -102,6 +102,9 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan
102 } 102 }
103 103
104 private loadWidgetConfig() { 104 private loadWidgetConfig() {
  105 + if (!this.widget) {
  106 + return;
  107 + }
105 const widgetInfo = this.widgetComponentService.getInstantWidgetInfo(this.widget); 108 const widgetInfo = this.widgetComponentService.getInstantWidgetInfo(this.widget);
106 const rawSettingsSchema = widgetInfo.typeSettingsSchema || widgetInfo.settingsSchema; 109 const rawSettingsSchema = widgetInfo.typeSettingsSchema || widgetInfo.settingsSchema;
107 const rawDataKeySettingsSchema = widgetInfo.typeDataKeySettingsSchema || widgetInfo.dataKeySettingsSchema; 110 const rawDataKeySettingsSchema = widgetInfo.typeDataKeySettingsSchema || widgetInfo.dataKeySettingsSchema;
@@ -16,10 +16,15 @@ @@ -16,10 +16,15 @@
16 16
17 --> 17 -->
18 <hotkeys-cheatsheet></hotkeys-cheatsheet> 18 <hotkeys-cheatsheet></hotkeys-cheatsheet>
  19 +<div fxLayout="column" class="tb-progress-cover" fxLayoutAlign="center center"
  20 + *ngIf="layoutCtx.widgets.isLoading()">
  21 + <mat-spinner color="warn" mode="indeterminate" diameter="100">
  22 + </mat-spinner>
  23 +</div>
19 <div class="mat-content" style="position: relative; width: 100%; height: 100%;" 24 <div class="mat-content" style="position: relative; width: 100%; height: 100%;"
20 [style.backgroundImage]="backgroundImage" 25 [style.backgroundImage]="backgroundImage"
21 [ngStyle]="dashboardStyle"> 26 [ngStyle]="dashboardStyle">
22 - <section *ngIf="layoutCtx.widgets.size() === 0" fxLayoutAlign="center center" 27 + <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center"
23 [ngStyle]="{'color': layoutCtx.gridSettings.color}" 28 [ngStyle]="{'color': layoutCtx.gridSettings.color}"
24 style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;" 29 style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;"
25 class="mat-headline tb-absolute-fill"> 30 class="mat-headline tb-absolute-fill">
@@ -31,8 +31,8 @@ @@ -31,8 +31,8 @@
31 <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> 31 <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
32 </button> 32 </button>
33 </div> 33 </div>
34 - <div fxFlex="0%" id="tb-javascript-panel" class="tb-js-func-panel" fxLayout="column" tb-toast toastTarget="jsFuncEditor">  
35 - <div fxFlex #javascriptEditor id="tb-javascript-input" [ngClass]="{'fill-height': fillHeight}"></div> 34 + <div id="tb-javascript-panel" class="tb-js-func-panel" fxLayout="column" tb-toast toastTarget="jsFuncEditor">
  35 + <div #javascriptEditor id="tb-javascript-input" [ngClass]="{'fill-height': fillHeight}"></div>
36 </div> 36 </div>
37 <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;"> 37 <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;">
38 <label class="tb-title no-padding">}</label> 38 <label class="tb-title no-padding">}</label>
@@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
30 <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> 30 <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
31 </button> 31 </button>
32 </div> 32 </div>
33 - <div fxFlex="0%" id="tb-json-panel" tb-toast toastTarget="jsonObjectEditor" 33 + <div id="tb-json-panel" tb-toast toastTarget="jsonObjectEditor"
34 class="tb-json-object-panel" fxLayout="column"> 34 class="tb-json-object-panel" fxLayout="column">
35 <div #jsonEditor id="tb-json-input" [ngStyle]="editorStyle" [ngClass]="{'fill-height': fillHeight}"></div> 35 <div #jsonEditor id="tb-json-input" [ngStyle]="editorStyle" [ngClass]="{'fill-height': fillHeight}"></div>
36 </div> 36 </div>
@@ -144,7 +144,7 @@ class DraggableChip { @@ -144,7 +144,7 @@ class DraggableChip {
144 const dataTransfer = event.dataTransfer; 144 const dataTransfer = event.dataTransfer;
145 dataTransfer.effectAllowed = 'copyMove'; 145 dataTransfer.effectAllowed = 'copyMove';
146 dataTransfer.dropEffect = 'move'; 146 dataTransfer.dropEffect = 'move';
147 - dataTransfer.setData('text/plain', this.index() + ''); 147 + dataTransfer.setData('text', this.index() + '');
148 } 148 }
149 } 149 }
150 150
@@ -222,7 +222,7 @@ class DraggableChip { @@ -222,7 +222,7 @@ class DraggableChip {
222 return; 222 return;
223 } 223 }
224 event = (event as any).originalEvent || event; 224 event = (event as any).originalEvent || event;
225 - const droppedItemIndex = parseInt(event.dataTransfer.getData('text/plain'), 10); 225 + const droppedItemIndex = parseInt(event.dataTransfer.getData('text'), 10);
226 const currentIndex = this.index(); 226 const currentIndex = this.index();
227 let newIndex; 227 let newIndex;
228 if (this.dropPosition === 'before') { 228 if (this.dropPosition === 'before') {
@@ -17,94 +17,92 @@ @@ -17,94 +17,92 @@
17 --> 17 -->
18 <form [formGroup]="timewindowForm" (ngSubmit)="update()"> 18 <form [formGroup]="timewindowForm" (ngSubmit)="update()">
19 <fieldset [disabled]="(isLoading$ | async)"> 19 <fieldset [disabled]="(isLoading$ | async)">
20 - <div class="mat-content" style="height: 100%;" fxFlex fxLayout="column">  
21 - <section fxLayout="column">  
22 - <mat-tab-group dynamicHeight [ngClass]="{'tb-headless': historyOnly}"  
23 - (selectedIndexChange)="timewindowForm.markAsDirty()" [(selectedIndex)]="timewindow.selectedTab">  
24 - <mat-tab label="{{ 'timewindow.realtime' | translate }}">  
25 - <div formGroupName="realtime" class="mat-content mat-padding" fxLayout="column">  
26 - <tb-timeinterval  
27 - formControlName="timewindowMs"  
28 - predefinedName="timewindow.last"  
29 - [required]="timewindow.selectedTab === timewindowTypes.REALTIME"  
30 - style="padding-top: 8px;"></tb-timeinterval>  
31 - </div>  
32 - </mat-tab>  
33 - <mat-tab label="{{ 'timewindow.history' | translate }}">  
34 - <div formGroupName="history" class="mat-content mat-padding" style="padding-top: 8px;">  
35 - <mat-radio-group formControlName="historyType">  
36 - <mat-radio-button [value]="historyTypes.LAST_INTERVAL" color="primary">  
37 - <section fxLayout="column">  
38 - <tb-timeinterval  
39 - formControlName="timewindowMs"  
40 - predefinedName="timewindow.last"  
41 - [fxShow]="timewindowForm.get('history').get('historyType').value === historyTypes.LAST_INTERVAL"  
42 - [required]="timewindow.selectedTab === timewindowTypes.HISTORY &&  
43 - timewindowForm.get('history').get('historyType').value === historyTypes.LAST_INTERVAL"  
44 - style="padding-top: 8px;"></tb-timeinterval>  
45 - </section>  
46 - </mat-radio-button>  
47 - <mat-radio-button [value]="historyTypes.FIXED" color="primary">  
48 - <section fxLayout="column">  
49 - <span translate>timewindow.time-period</span>  
50 - <tb-datetime-period  
51 - formControlName="fixedTimewindow"  
52 - [fxShow]="timewindowForm.get('history').get('historyType').value === historyTypes.FIXED"  
53 - [required]="timewindow.selectedTab === timewindowTypes.HISTORY &&  
54 - timewindowForm.get('history').get('historyType').value === historyTypes.FIXED"  
55 - style="padding-top: 8px;"></tb-datetime-period>  
56 - </section>  
57 - </mat-radio-button>  
58 - </mat-radio-group>  
59 - </div>  
60 - </mat-tab>  
61 - </mat-tab-group>  
62 - <div *ngIf="aggregation" formGroupName="aggregation" class="mat-content mat-padding" fxLayout="column">  
63 - <mat-form-field>  
64 - <mat-label translate>aggregation.function</mat-label>  
65 - <mat-select matInput formControlName="type" style="min-width: 150px;">  
66 - <mat-option *ngFor="let aggregation of aggregations" [value]="aggregation">  
67 - {{ aggregationTypesTranslations.get(aggregation) | translate }}  
68 - </mat-option>  
69 - </mat-select>  
70 - </mat-form-field>  
71 - <div *ngIf="timewindowForm.get('aggregation').get('type').value === aggregationTypes.NONE"  
72 - class="limit-slider-container"  
73 - fxLayout="row" fxLayoutAlign="start center">  
74 - <span translate>aggregation.limit</span>  
75 - <mat-slider fxFlex formControlName="limit"  
76 - thumbLabel  
77 - [value]="timewindowForm.get('aggregation').get('limit').value"  
78 - min="{{minDatapointsLimit()}}"  
79 - max="{{maxDatapointsLimit()}}">  
80 - </mat-slider>  
81 - <mat-form-field style="max-width: 80px;">  
82 - <input matInput formControlName="limit" type="number" step="1"  
83 - [value]="timewindowForm.get('aggregation').get('limit').value"  
84 - min="{{minDatapointsLimit()}}"  
85 - max="{{maxDatapointsLimit()}}"/>  
86 - </mat-form-field> 20 + <div class="mat-content" style="height: 100%;" fxLayout="column">
  21 + <mat-tab-group dynamicHeight [ngClass]="{'tb-headless': historyOnly}"
  22 + (selectedIndexChange)="timewindowForm.markAsDirty()" [(selectedIndex)]="timewindow.selectedTab">
  23 + <mat-tab label="{{ 'timewindow.realtime' | translate }}">
  24 + <div formGroupName="realtime" class="mat-content mat-padding" fxLayout="column">
  25 + <tb-timeinterval
  26 + formControlName="timewindowMs"
  27 + predefinedName="timewindow.last"
  28 + [required]="timewindow.selectedTab === timewindowTypes.REALTIME"
  29 + style="padding-top: 8px;"></tb-timeinterval>
87 </div> 30 </div>
  31 + </mat-tab>
  32 + <mat-tab label="{{ 'timewindow.history' | translate }}">
  33 + <div formGroupName="history" class="mat-content mat-padding" style="padding-top: 8px;">
  34 + <mat-radio-group formControlName="historyType">
  35 + <mat-radio-button [value]="historyTypes.LAST_INTERVAL" color="primary">
  36 + <section fxLayout="column">
  37 + <tb-timeinterval
  38 + formControlName="timewindowMs"
  39 + predefinedName="timewindow.last"
  40 + [fxShow]="timewindowForm.get('history').get('historyType').value === historyTypes.LAST_INTERVAL"
  41 + [required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
  42 + timewindowForm.get('history').get('historyType').value === historyTypes.LAST_INTERVAL"
  43 + style="padding-top: 8px;"></tb-timeinterval>
  44 + </section>
  45 + </mat-radio-button>
  46 + <mat-radio-button [value]="historyTypes.FIXED" color="primary">
  47 + <section fxLayout="column">
  48 + <span translate>timewindow.time-period</span>
  49 + <tb-datetime-period
  50 + formControlName="fixedTimewindow"
  51 + [fxShow]="timewindowForm.get('history').get('historyType').value === historyTypes.FIXED"
  52 + [required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
  53 + timewindowForm.get('history').get('historyType').value === historyTypes.FIXED"
  54 + style="padding-top: 8px;"></tb-datetime-period>
  55 + </section>
  56 + </mat-radio-button>
  57 + </mat-radio-group>
  58 + </div>
  59 + </mat-tab>
  60 + </mat-tab-group>
  61 + <div *ngIf="aggregation" formGroupName="aggregation" class="mat-content mat-padding" fxLayout="column">
  62 + <mat-form-field>
  63 + <mat-label translate>aggregation.function</mat-label>
  64 + <mat-select matInput formControlName="type" style="min-width: 150px;">
  65 + <mat-option *ngFor="let aggregation of aggregations" [value]="aggregation">
  66 + {{ aggregationTypesTranslations.get(aggregation) | translate }}
  67 + </mat-option>
  68 + </mat-select>
  69 + </mat-form-field>
  70 + <div *ngIf="timewindowForm.get('aggregation').get('type').value === aggregationTypes.NONE"
  71 + class="limit-slider-container"
  72 + fxLayout="row" fxLayoutAlign="start center">
  73 + <span translate>aggregation.limit</span>
  74 + <mat-slider fxFlex formControlName="limit"
  75 + thumbLabel
  76 + [value]="timewindowForm.get('aggregation').get('limit').value"
  77 + min="{{minDatapointsLimit()}}"
  78 + max="{{maxDatapointsLimit()}}">
  79 + </mat-slider>
  80 + <mat-form-field style="max-width: 80px;">
  81 + <input matInput formControlName="limit" type="number" step="1"
  82 + [value]="timewindowForm.get('aggregation').get('limit').value"
  83 + min="{{minDatapointsLimit()}}"
  84 + max="{{maxDatapointsLimit()}}"/>
  85 + </mat-form-field>
88 </div> 86 </div>
89 - <div formGroupName="realtime"  
90 - *ngIf="aggregation && timewindowForm.get('aggregation').get('type').value !== aggregationTypes.NONE &&  
91 - timewindow.selectedTab === timewindowTypes.REALTIME" class="mat-content mat-padding" fxLayout="column">  
92 - <tb-timeinterval  
93 - formControlName="interval"  
94 - [min]="minRealtimeAggInterval()" [max]="maxRealtimeAggInterval()"  
95 - predefinedName="aggregation.group-interval">  
96 - </tb-timeinterval>  
97 - </div>  
98 - <div formGroupName="history"  
99 - *ngIf="aggregation && timewindowForm.get('aggregation').get('type').value !== aggregationTypes.NONE &&  
100 - timewindow.selectedTab === timewindowTypes.HISTORY" class="mat-content mat-padding" fxLayout="column">  
101 - <tb-timeinterval  
102 - formControlName="interval"  
103 - [min]="minHistoryAggInterval()" [max]="maxHistoryAggInterval()"  
104 - predefinedName="aggregation.group-interval">  
105 - </tb-timeinterval>  
106 - </div>  
107 - </section> 87 + </div>
  88 + <div formGroupName="realtime"
  89 + *ngIf="aggregation && timewindowForm.get('aggregation').get('type').value !== aggregationTypes.NONE &&
  90 + timewindow.selectedTab === timewindowTypes.REALTIME" class="mat-content mat-padding" fxLayout="column">
  91 + <tb-timeinterval
  92 + formControlName="interval"
  93 + [min]="minRealtimeAggInterval()" [max]="maxRealtimeAggInterval()"
  94 + predefinedName="aggregation.group-interval">
  95 + </tb-timeinterval>
  96 + </div>
  97 + <div formGroupName="history"
  98 + *ngIf="aggregation && timewindowForm.get('aggregation').get('type').value !== aggregationTypes.NONE &&
  99 + timewindow.selectedTab === timewindowTypes.HISTORY" class="mat-content mat-padding" fxLayout="column">
  100 + <tb-timeinterval
  101 + formControlName="interval"
  102 + [min]="minHistoryAggInterval()" [max]="maxHistoryAggInterval()"
  103 + predefinedName="aggregation.group-interval">
  104 + </tb-timeinterval>
  105 + </div>
108 <span fxFlex></span> 106 <span fxFlex></span>
109 <div fxLayout="row" class="tb-panel-actions"> 107 <div fxLayout="row" class="tb-panel-actions">
110 <span fxFlex></span> 108 <span fxFlex></span>