Showing
18 changed files
with
279 additions
and
240 deletions
... | ... | @@ -184,6 +184,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo |
184 | 184 | swap: false, |
185 | 185 | maxRows: 100, |
186 | 186 | minCols: this.columns ? this.columns : 24, |
187 | + maxCols: 3000, | |
188 | + maxItemCols: 1000, | |
189 | + maxItemRows: 1000, | |
190 | + maxItemArea: 1000000, | |
187 | 191 | outerMargin: true, |
188 | 192 | margin: this.margin ? this.margin : 10, |
189 | 193 | minItemCols: 1, | ... | ... |
... | ... | @@ -299,14 +299,16 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni |
299 | 299 | |
300 | 300 | writeValue(obj: WidgetActionsData): void { |
301 | 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 | 314 | private onActionsUpdated() { | ... | ... |
... | ... | @@ -29,7 +29,7 @@ |
29 | 29 | <div class="tb-color-preview" (click)="showColorPicker(key)" style="margin-right: 5px;"> |
30 | 30 | <div class="tb-color-result" [ngStyle]="{background: key.color}"></div> |
31 | 31 | </div> |
32 | - <div style="flex: 1; min-width: 0px;" fxLayout="row"> | |
32 | + <div class="tb-chip-labels"> | |
33 | 33 | <div class="tb-chip-label"> |
34 | 34 | <span *ngIf="datasourceType !== datasourceTypes.function && widgetType !== widgetTypes.alarm"> |
35 | 35 | <span *ngIf="key.type === dataKeyTypes.attribute" | ... | ... |
... | ... | @@ -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 | 48 | .mat-chip-remove.mat-icon { | ... | ... |
... | ... | @@ -38,16 +38,16 @@ |
38 | 38 | </mat-option> |
39 | 39 | </mat-select> |
40 | 40 | </mat-form-field> |
41 | - <mat-checkbox fxFlex formControlName="showMin"> | |
41 | + <mat-checkbox formControlName="showMin"> | |
42 | 42 | {{ 'legend.show-min' | translate }} |
43 | 43 | </mat-checkbox> |
44 | - <mat-checkbox fxFlex formControlName="showMax"> | |
44 | + <mat-checkbox formControlName="showMax"> | |
45 | 45 | {{ 'legend.show-max' | translate }} |
46 | 46 | </mat-checkbox> |
47 | - <mat-checkbox fxFlex formControlName="showAvg"> | |
47 | + <mat-checkbox formControlName="showAvg"> | |
48 | 48 | {{ 'legend.show-avg' | translate }} |
49 | 49 | </mat-checkbox> |
50 | - <mat-checkbox fxFlex formControlName="showTotal"> | |
50 | + <mat-checkbox formControlName="showTotal"> | |
51 | 51 | {{ 'legend.show-total' | translate }} |
52 | 52 | </mat-checkbox> |
53 | 53 | </section> | ... | ... |
... | ... | @@ -22,11 +22,10 @@ |
22 | 22 | <div *ngIf="widgetType === widgetTypes.timeseries || widgetType === widgetTypes.alarm" |
23 | 23 | fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center"> |
24 | 24 | <div fxLayout="column" fxLayoutGap="8px" fxFlex> |
25 | - <mat-checkbox fxFlex formControlName="useDashboardTimewindow"> | |
25 | + <mat-checkbox formControlName="useDashboardTimewindow"> | |
26 | 26 | {{ 'widget-config.use-dashboard-timewindow' | translate }} |
27 | 27 | </mat-checkbox> |
28 | - <mat-checkbox fxFlex | |
29 | - formControlName="displayTimewindow"> | |
28 | + <mat-checkbox formControlName="displayTimewindow"> | |
30 | 29 | {{ 'widget-config.display-timewindow' | translate }} |
31 | 30 | </mat-checkbox> |
32 | 31 | </div> | ... | ... |
... | ... | @@ -163,6 +163,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont |
163 | 163 | } else { |
164 | 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 | 170 | this.widgetSettings = this.fb.group({ |
167 | 171 | title: [null, []], |
168 | 172 | showTitleIcon: [null, []], |
... | ... | @@ -574,7 +578,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont |
574 | 578 | } |
575 | 579 | |
576 | 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 | 584 | public removeDatasource(index: number) { |
... | ... | @@ -772,7 +776,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont |
772 | 776 | valid: false |
773 | 777 | } |
774 | 778 | }; |
775 | - } else { | |
779 | + } else if (this.modelValue) { | |
776 | 780 | const config = this.modelValue.config; |
777 | 781 | if (this.widgetType === widgetType.rpc && this.modelValue.isDataEnabled) { |
778 | 782 | if (!config.targetDeviceAliasIds || !config.targetDeviceAliasIds.length) { | ... | ... |
... | ... | @@ -120,59 +120,90 @@ |
120 | 120 | 'tb-dashboard-toolbar-opened': toolbarOpened, |
121 | 121 | 'tb-dashboard-toolbar-animated': isToolbarOpenedAnimate, |
122 | 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 | 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 | 204 | mode="over" |
174 | 205 | position="end"> |
175 | - <tb-details-panel *ngIf="isEditingWidget" fxFlex | |
206 | + <tb-details-panel *ngIf="!isEditingWidgetClosed" fxFlex | |
176 | 207 | headerTitle="{{editingWidget?.config.title}}" |
177 | 208 | headerSubtitle="{{ editingWidgetSubtitle }}" |
178 | 209 | [isReadOnly]="false" |
... | ... | @@ -185,21 +216,14 @@ |
185 | 216 | <div [tb-help]="helpLinkIdForWidgetType()"></div> |
186 | 217 | </div> |
187 | 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 | 224 | </tb-edit-widget> |
194 | 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 | 227 | headerTitle="{{'dashboard.select-widget-title' | translate}}" |
204 | 228 | headerHeightPx="120" |
205 | 229 | [isReadOnly]="true" |
... | ... | @@ -218,40 +242,12 @@ |
218 | 242 | </div> |
219 | 243 | </div> |
220 | 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 | 248 | </tb-dashboard-widget-select> |
225 | 249 | </tb-details-panel> |
226 | 250 | </mat-drawer> |
227 | 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 | 252 | </section> |
257 | 253 | </div> | ... | ... |
... | ... | @@ -32,9 +32,9 @@ div.tb-dashboard-page { |
32 | 32 | background-color: #eee; |
33 | 33 | } |
34 | 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 | 38 | mat-form-field { |
39 | 39 | .mat-form-field-infix { |
40 | 40 | width: 100%; |
... | ... | @@ -47,17 +47,6 @@ div.tb-dashboard-page { |
47 | 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 | 51 | section.tb-dashboard-toolbar { |
63 | 52 | position: absolute; |
... | ... | @@ -106,21 +95,16 @@ div.tb-dashboard-page { |
106 | 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 | 108 | mat-drawer.tb-details-drawer { |
125 | 109 | @media #{$mat-gt-sm} { |
126 | 110 | width: 85% !important; |
... | ... | @@ -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 | 123 | section.tb-powered-by-footer { |
144 | 124 | position: absolute; |
145 | 125 | right: 25px; | ... | ... |
... | ... | @@ -104,9 +104,11 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
104 | 104 | isFullscreen = false; |
105 | 105 | isEdit = false; |
106 | 106 | isEditingWidget = false; |
107 | + isEditingWidgetClosed = true; | |
107 | 108 | isMobile = !this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']); |
108 | 109 | forceDashboardMobileMode = false; |
109 | 110 | isAddingWidget = false; |
111 | + isAddingWidgetClosed = true; | |
110 | 112 | widgetsBundle: WidgetsBundle = null; |
111 | 113 | |
112 | 114 | isToolbarOpened = false; |
... | ... | @@ -284,8 +286,10 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
284 | 286 | this.isFullscreen = false; |
285 | 287 | this.isEdit = false; |
286 | 288 | this.isEditingWidget = false; |
289 | + this.isEditingWidgetClosed = true; | |
287 | 290 | this.forceDashboardMobileMode = false; |
288 | 291 | this.isAddingWidget = false; |
292 | + this.isAddingWidgetClosed = true; | |
289 | 293 | this.widgetsBundle = null; |
290 | 294 | |
291 | 295 | this.isToolbarOpened = false; |
... | ... | @@ -694,6 +698,31 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
694 | 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 | 726 | private addWidgetToLayout(widget: Widget, layoutId: DashboardLayoutId) { |
698 | 727 | this.dashboardUtils.addWidgetToLayout(this.dashboard, this.dashboardCtx.state, layoutId, widget); |
699 | 728 | this.layouts[layoutId].layoutCtx.widgets.addWidgetId(widget.id); | ... | ... |
... | ... | @@ -77,6 +77,8 @@ export class LayoutWidgetsArray implements Iterable<Widget> { |
77 | 77 | private widgetIds: string[] = []; |
78 | 78 | private pointer = 0; |
79 | 79 | |
80 | + private loaded = false; | |
81 | + | |
80 | 82 | constructor(private dashboard: Dashboard) { |
81 | 83 | } |
82 | 84 | |
... | ... | @@ -84,8 +86,17 @@ export class LayoutWidgetsArray implements Iterable<Widget> { |
84 | 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 | 97 | setWidgetIds(widgetIds: string[]) { |
88 | 98 | this.widgetIds = widgetIds; |
99 | + this.loaded = true; | |
89 | 100 | } |
90 | 101 | |
91 | 102 | addWidgetId(widgetId: string) { | ... | ... |
... | ... | @@ -102,6 +102,9 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan |
102 | 102 | } |
103 | 103 | |
104 | 104 | private loadWidgetConfig() { |
105 | + if (!this.widget) { | |
106 | + return; | |
107 | + } | |
105 | 108 | const widgetInfo = this.widgetComponentService.getInstantWidgetInfo(this.widget); |
106 | 109 | const rawSettingsSchema = widgetInfo.typeSettingsSchema || widgetInfo.settingsSchema; |
107 | 110 | const rawDataKeySettingsSchema = widgetInfo.typeDataKeySettingsSchema || widgetInfo.dataKeySettingsSchema; | ... | ... |
... | ... | @@ -16,10 +16,15 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 24 | <div class="mat-content" style="position: relative; width: 100%; height: 100%;" |
20 | 25 | [style.backgroundImage]="backgroundImage" |
21 | 26 | [ngStyle]="dashboardStyle"> |
22 | - <section *ngIf="layoutCtx.widgets.size() === 0" fxLayoutAlign="center center" | |
27 | + <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center" | |
23 | 28 | [ngStyle]="{'color': layoutCtx.gridSettings.color}" |
24 | 29 | style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;" |
25 | 30 | class="mat-headline tb-absolute-fill"> | ... | ... |
... | ... | @@ -31,8 +31,8 @@ |
31 | 31 | <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> |
32 | 32 | </button> |
33 | 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 | 36 | </div> |
37 | 37 | <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;"> |
38 | 38 | <label class="tb-title no-padding">}</label> | ... | ... |
... | ... | @@ -30,7 +30,7 @@ |
30 | 30 | <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon> |
31 | 31 | </button> |
32 | 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 | 34 | class="tb-json-object-panel" fxLayout="column"> |
35 | 35 | <div #jsonEditor id="tb-json-input" [ngStyle]="editorStyle" [ngClass]="{'fill-height': fillHeight}"></div> |
36 | 36 | </div> | ... | ... |
... | ... | @@ -144,7 +144,7 @@ class DraggableChip { |
144 | 144 | const dataTransfer = event.dataTransfer; |
145 | 145 | dataTransfer.effectAllowed = 'copyMove'; |
146 | 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 | 222 | return; |
223 | 223 | } |
224 | 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 | 226 | const currentIndex = this.index(); |
227 | 227 | let newIndex; |
228 | 228 | if (this.dropPosition === 'before') { | ... | ... |
... | ... | @@ -17,94 +17,92 @@ |
17 | 17 | --> |
18 | 18 | <form [formGroup]="timewindowForm" (ngSubmit)="update()"> |
19 | 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 | 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 | 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 | 106 | <span fxFlex></span> |
109 | 107 | <div fxLayout="row" class="tb-panel-actions"> |
110 | 108 | <span fxFlex></span> | ... | ... |