Showing
25 changed files
with
470 additions
and
108 deletions
@@ -395,7 +395,7 @@ export class UtilsService { | @@ -395,7 +395,7 @@ export class UtilsService { | ||
395 | } else if (variableName === 'deviceName') { | 395 | } else if (variableName === 'deviceName') { |
396 | label = label.split(variable).join(datasource.entityName); | 396 | label = label.split(variable).join(datasource.entityName); |
397 | } else if (variableName === 'entityLabel') { | 397 | } else if (variableName === 'entityLabel') { |
398 | - label = label.split(variable).join(datasource.entityLabel); | 398 | + label = label.split(variable).join(datasource.entityLabel || datasource.entityName); |
399 | } else if (variableName === 'aliasName') { | 399 | } else if (variableName === 'aliasName') { |
400 | label = label.split(variable).join(datasource.aliasName); | 400 | label = label.split(variable).join(datasource.aliasName); |
401 | } else if (variableName === 'entityDescription') { | 401 | } else if (variableName === 'entityDescription') { |
@@ -82,14 +82,19 @@ | @@ -82,14 +82,19 @@ | ||
82 | <div *ngIf="widget.hasWidgetTitleTemplate"> | 82 | <div *ngIf="widget.hasWidgetTitleTemplate"> |
83 | TODO: | 83 | TODO: |
84 | </div> | 84 | </div> |
85 | - <span [fxShow]="widget.showTitle" [ngStyle]="widget.titleStyle" class="mat-subheading-2 title"> | 85 | + <span [fxShow]="widget.showTitle" |
86 | + [ngStyle]="widget.titleStyle" | ||
87 | + [matTooltip]="widget.titleTooltip" | ||
88 | + matTooltipPosition="above" | ||
89 | + class="mat-subheading-2 title"> | ||
86 | <mat-icon *ngIf="widget.showTitleIcon" [ngStyle]="widget.titleIconStyle">{{widget.titleIcon}}</mat-icon> | 90 | <mat-icon *ngIf="widget.showTitleIcon" [ngStyle]="widget.titleIconStyle">{{widget.titleIcon}}</mat-icon> |
87 | {{widget.title}} | 91 | {{widget.title}} |
88 | </span> | 92 | </span> |
89 | <tb-timewindow *ngIf="widget.hasTimewindow" | 93 | <tb-timewindow *ngIf="widget.hasTimewindow" |
90 | #timewindowComponent | 94 | #timewindowComponent |
91 | aggregation="{{widget.hasAggregation}}" | 95 | aggregation="{{widget.hasAggregation}}" |
92 | - [ngModel]="widgetComponent.widget.config.timewindow" | 96 | + [isEdit]="isEdit" |
97 | + [(ngModel)]="widgetComponent.widget.config.timewindow" | ||
93 | (ngModelChange)="widgetComponent.onTimewindowChanged($event)"> | 98 | (ngModelChange)="widgetComponent.onTimewindowChanged($event)"> |
94 | </tb-timewindow> | 99 | </tb-timewindow> |
95 | </div> | 100 | </div> |
@@ -32,6 +32,7 @@ | @@ -32,6 +32,7 @@ | ||
32 | <section fxFlex fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 16px;"> | 32 | <section fxFlex fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 16px;"> |
33 | <span [ngClass]="{'tb-disabled-label': dataSettings.get('useDashboardTimewindow').value}" translate style="padding-right: 8px;">widget-config.timewindow</span> | 33 | <span [ngClass]="{'tb-disabled-label': dataSettings.get('useDashboardTimewindow').value}" translate style="padding-right: 8px;">widget-config.timewindow</span> |
34 | <tb-timewindow asButton="true" | 34 | <tb-timewindow asButton="true" |
35 | + isEdit="true" | ||
35 | aggregation="{{ widgetType === widgetTypes.timeseries }}" | 36 | aggregation="{{ widgetType === widgetTypes.timeseries }}" |
36 | fxFlex formControlName="timewindow"></tb-timewindow> | 37 | fxFlex formControlName="timewindow"></tb-timewindow> |
37 | </section> | 38 | </section> |
@@ -307,6 +308,10 @@ | @@ -307,6 +308,10 @@ | ||
307 | <mat-label translate>widget-config.icon-size</mat-label> | 308 | <mat-label translate>widget-config.icon-size</mat-label> |
308 | <input matInput formControlName="iconSize"> | 309 | <input matInput formControlName="iconSize"> |
309 | </mat-form-field> | 310 | </mat-form-field> |
311 | + <mat-form-field fxFlex> | ||
312 | + <mat-label translate>widget-config.title-tooltip</mat-label> | ||
313 | + <input matInput formControlName="titleTooltip"> | ||
314 | + </mat-form-field> | ||
310 | </div> | 315 | </div> |
311 | <div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" | 316 | <div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" |
312 | fxLayoutGap="8px"> | 317 | fxLayoutGap="8px"> |
@@ -173,6 +173,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont | @@ -173,6 +173,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont | ||
173 | titleIcon: [null, []], | 173 | titleIcon: [null, []], |
174 | iconColor: [null, []], | 174 | iconColor: [null, []], |
175 | iconSize: [null, []], | 175 | iconSize: [null, []], |
176 | + titleTooltip: [null, []], | ||
176 | showTitle: [null, []], | 177 | showTitle: [null, []], |
177 | dropShadow: [null, []], | 178 | dropShadow: [null, []], |
178 | enableFullscreen: [null, []], | 179 | enableFullscreen: [null, []], |
@@ -344,6 +345,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont | @@ -344,6 +345,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont | ||
344 | titleIcon: isDefined(config.titleIcon) ? config.titleIcon : '', | 345 | titleIcon: isDefined(config.titleIcon) ? config.titleIcon : '', |
345 | iconColor: isDefined(config.iconColor) ? config.iconColor : 'rgba(0, 0, 0, 0.87)', | 346 | iconColor: isDefined(config.iconColor) ? config.iconColor : 'rgba(0, 0, 0, 0.87)', |
346 | iconSize: isDefined(config.iconSize) ? config.iconSize : '24px', | 347 | iconSize: isDefined(config.iconSize) ? config.iconSize : '24px', |
348 | + titleTooltip: isDefined(config.titleTooltip) ? config.titleTooltip : '', | ||
347 | showTitle: config.showTitle, | 349 | showTitle: config.showTitle, |
348 | dropShadow: isDefined(config.dropShadow) ? config.dropShadow : true, | 350 | dropShadow: isDefined(config.dropShadow) ? config.dropShadow : true, |
349 | enableFullscreen: isDefined(config.enableFullscreen) ? config.enableFullscreen : true, | 351 | enableFullscreen: isDefined(config.enableFullscreen) ? config.enableFullscreen : true, |
@@ -1052,9 +1052,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | @@ -1052,9 +1052,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | ||
1052 | if (e.id) { | 1052 | if (e.id) { |
1053 | const descriptors = this.getActionDescriptors('elementClick'); | 1053 | const descriptors = this.getActionDescriptors('elementClick'); |
1054 | if (descriptors.length) { | 1054 | if (descriptors.length) { |
1055 | - $event.stopPropagation(); | ||
1056 | descriptors.forEach((descriptor) => { | 1055 | descriptors.forEach((descriptor) => { |
1057 | if (descriptor.name === e.id) { | 1056 | if (descriptor.name === e.id) { |
1057 | + $event.stopPropagation(); | ||
1058 | const entityInfo = this.getActiveEntityInfo(); | 1058 | const entityInfo = this.getActiveEntityInfo(); |
1059 | const entityId = entityInfo ? entityInfo.entityId : null; | 1059 | const entityId = entityInfo ? entityInfo.entityId : null; |
1060 | const entityName = entityInfo ? entityInfo.entityName : null; | 1060 | const entityName = entityInfo ? entityInfo.entityName : null; |
@@ -295,6 +295,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { | @@ -295,6 +295,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { | ||
295 | margin: string; | 295 | margin: string; |
296 | 296 | ||
297 | title: string; | 297 | title: string; |
298 | + titleTooltip: string; | ||
298 | showTitle: boolean; | 299 | showTitle: boolean; |
299 | titleStyle: {[klass: string]: any}; | 300 | titleStyle: {[klass: string]: any}; |
300 | 301 | ||
@@ -360,6 +361,8 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { | @@ -360,6 +361,8 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { | ||
360 | 361 | ||
361 | this.title = isDefined(this.widgetContext.widgetTitle) | 362 | this.title = isDefined(this.widgetContext.widgetTitle) |
362 | && this.widgetContext.widgetTitle.length ? this.widgetContext.widgetTitle : this.widget.config.title; | 363 | && this.widgetContext.widgetTitle.length ? this.widgetContext.widgetTitle : this.widget.config.title; |
364 | + this.titleTooltip = isDefined(this.widgetContext.widgetTitleTooltip) | ||
365 | + && this.widgetContext.widgetTitleTooltip.length ? this.widgetContext.widgetTitleTooltip : this.widget.config.titleTooltip; | ||
363 | this.showTitle = isDefined(this.widget.config.showTitle) ? this.widget.config.showTitle : true; | 366 | this.showTitle = isDefined(this.widget.config.showTitle) ? this.widget.config.showTitle : true; |
364 | this.titleStyle = this.widget.config.titleStyle ? this.widget.config.titleStyle : {}; | 367 | this.titleStyle = this.widget.config.titleStyle ? this.widget.config.titleStyle : {}; |
365 | 368 |
@@ -178,6 +178,7 @@ export class WidgetContext { | @@ -178,6 +178,7 @@ export class WidgetContext { | ||
178 | 178 | ||
179 | widgetTitleTemplate?: string; | 179 | widgetTitleTemplate?: string; |
180 | widgetTitle?: string; | 180 | widgetTitle?: string; |
181 | + widgetTitleTooltip?: string; | ||
181 | customHeaderActions?: Array<WidgetHeaderAction>; | 182 | customHeaderActions?: Array<WidgetHeaderAction>; |
182 | widgetActions?: Array<WidgetAction>; | 183 | widgetActions?: Array<WidgetAction>; |
183 | 184 |
@@ -55,6 +55,7 @@ | @@ -55,6 +55,7 @@ | ||
55 | </button> | 55 | </button> |
56 | <tb-timewindow [fxShow]="isEdit || displayDashboardTimewindow()" | 56 | <tb-timewindow [fxShow]="isEdit || displayDashboardTimewindow()" |
57 | isToolbar="true" | 57 | isToolbar="true" |
58 | + [isEdit]="isEdit" | ||
58 | direction="left" | 59 | direction="left" |
59 | tooltipPosition="below" | 60 | tooltipPosition="below" |
60 | aggregation="true" | 61 | aggregation="true" |
@@ -87,6 +87,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato | @@ -87,6 +87,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato | ||
87 | }, | 87 | }, |
88 | onModelChange: this.onModelChange.bind(this), | 88 | onModelChange: this.onModelChange.bind(this), |
89 | onColorClick: this.onColorClick.bind(this), | 89 | onColorClick: this.onColorClick.bind(this), |
90 | + onIconClick: this.onIconClick.bind(this), | ||
90 | onToggleFullscreen: this.onToggleFullscreen.bind(this) | 91 | onToggleFullscreen: this.onToggleFullscreen.bind(this) |
91 | }; | 92 | }; |
92 | 93 | ||
@@ -201,6 +202,16 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato | @@ -201,6 +202,16 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato | ||
201 | }); | 202 | }); |
202 | } | 203 | } |
203 | 204 | ||
205 | + private onIconClick(key: (string | number)[], | ||
206 | + val: string, | ||
207 | + iconSelectedFn: (icon: string) => void) { | ||
208 | + this.dialogs.materialIconPicker(val).subscribe((icon) => { | ||
209 | + if (icon && iconSelectedFn) { | ||
210 | + iconSelectedFn(icon); | ||
211 | + } | ||
212 | + }); | ||
213 | + } | ||
214 | + | ||
204 | private onToggleFullscreen(element: HTMLElement, fullscreenFinishFn?: () => void) { | 215 | private onToggleFullscreen(element: HTMLElement, fullscreenFinishFn?: () => void) { |
205 | this.targetFullscreenElement = element; | 216 | this.targetFullscreenElement = element; |
206 | this.isFullscreen = !this.isFullscreen; | 217 | this.isFullscreen = !this.isFullscreen; |
@@ -143,7 +143,7 @@ class ThingsboardArray extends React.Component<JsonFormFieldProps, ThingsboardAr | @@ -143,7 +143,7 @@ class ThingsboardArray extends React.Component<JsonFormFieldProps, ThingsboardAr | ||
143 | const forms = (this.props.form.items as JsonFormData[]).map((form, index) => { | 143 | const forms = (this.props.form.items as JsonFormData[]).map((form, index) => { |
144 | const copy = this.copyWithIndex(form, i); | 144 | const copy = this.copyWithIndex(form, i); |
145 | return this.props.builder(copy, this.props.model, index, this.props.onChange, | 145 | return this.props.builder(copy, this.props.model, index, this.props.onChange, |
146 | - this.props.onColorClick, this.props.onToggleFullscreen, this.props.mapper); | 146 | + this.props.onColorClick, this.props.onIconClick, this.props.onToggleFullscreen, this.props.mapper); |
147 | }); | 147 | }); |
148 | arrays.push( | 148 | arrays.push( |
149 | <li key={keys[i]} className='list-group-item'> | 149 | <li key={keys[i]} className='list-group-item'> |
@@ -25,7 +25,7 @@ class ThingsboardFieldSet extends React.Component<JsonFormFieldProps, JsonFormFi | @@ -25,7 +25,7 @@ class ThingsboardFieldSet extends React.Component<JsonFormFieldProps, JsonFormFi | ||
25 | render() { | 25 | render() { |
26 | const forms = (this.props.form.items as JsonFormData[]).map((form: JsonFormData, index) => { | 26 | const forms = (this.props.form.items as JsonFormData[]).map((form: JsonFormData, index) => { |
27 | return this.props.builder(form, this.props.model, index, this.props.onChange, | 27 | return this.props.builder(form, this.props.model, index, this.props.onChange, |
28 | - this.props.onColorClick, this.props.onToggleFullscreen, this.props.mapper); | 28 | + this.props.onColorClick, this.props.onIconClick, this.props.onToggleFullscreen, this.props.mapper); |
29 | }); | 29 | }); |
30 | 30 | ||
31 | return ( | 31 | return ( |
1 | +/* | ||
2 | + * Copyright © 2016-2020 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 | +import * as React from 'react'; | ||
17 | +import * as ReactDOM from 'react-dom'; | ||
18 | +import ThingsboardBaseComponent from './json-form-base-component'; | ||
19 | +import reactCSS from 'reactcss'; | ||
20 | +import TextField from '@material-ui/core/TextField'; | ||
21 | +import IconButton from '@material-ui/core/IconButton'; | ||
22 | +import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; | ||
23 | +import ClearIcon from '@material-ui/icons/Clear'; | ||
24 | +import Icon from '@material-ui/core/Icon'; | ||
25 | +import Tooltip from '@material-ui/core/Tooltip'; | ||
26 | + | ||
27 | +interface ThingsboardIconState extends JsonFormFieldState { | ||
28 | + icon: string | null; | ||
29 | + focused: boolean; | ||
30 | +} | ||
31 | + | ||
32 | +class ThingsboardIcon extends React.Component<JsonFormFieldProps, ThingsboardIconState> { | ||
33 | + | ||
34 | + constructor(props) { | ||
35 | + super(props); | ||
36 | + this.onBlur = this.onBlur.bind(this); | ||
37 | + this.onFocus = this.onFocus.bind(this); | ||
38 | + this.onValueChanged = this.onValueChanged.bind(this); | ||
39 | + this.onIconClick = this.onIconClick.bind(this); | ||
40 | + this.onClear = this.onClear.bind(this); | ||
41 | + const icon = props.value ? props.value : ''; | ||
42 | + this.state = { | ||
43 | + icon, | ||
44 | + focused: false | ||
45 | + }; | ||
46 | + } | ||
47 | + | ||
48 | + onBlur() { | ||
49 | + this.setState({focused: false}); | ||
50 | + } | ||
51 | + | ||
52 | + onFocus() { | ||
53 | + this.setState({focused: true}); | ||
54 | + } | ||
55 | + | ||
56 | + componentDidMount() { | ||
57 | + const node = ReactDOM.findDOMNode(this); | ||
58 | + const iconContainer = $(node).children('#icon-container'); | ||
59 | + iconContainer.click((event) => { | ||
60 | + if (!this.props.form.readonly) { | ||
61 | + this.onIconClick(event); | ||
62 | + } | ||
63 | + }); | ||
64 | + } | ||
65 | + | ||
66 | + componentWillUnmount () { | ||
67 | + const node = ReactDOM.findDOMNode(this); | ||
68 | + const iconContainer = $(node).children('#icon-container'); | ||
69 | + iconContainer.off( 'click' ); | ||
70 | + } | ||
71 | + | ||
72 | + onValueChanged(value: string | null) { | ||
73 | + const icon = value; | ||
74 | + this.setState({ | ||
75 | + icon: value | ||
76 | + }); | ||
77 | + this.props.onChange(this.props.form.key, value); | ||
78 | + } | ||
79 | + | ||
80 | + onIconClick(event) { | ||
81 | + this.props.onIconClick(this.props.form.key, this.state.icon, | ||
82 | + (color) => { | ||
83 | + this.onValueChanged(color); | ||
84 | + } | ||
85 | + ); | ||
86 | + } | ||
87 | + | ||
88 | + onClear(event) { | ||
89 | + if (event) { | ||
90 | + event.stopPropagation(); | ||
91 | + } | ||
92 | + this.onValueChanged(''); | ||
93 | + } | ||
94 | + | ||
95 | + render() { | ||
96 | + | ||
97 | + const styles = reactCSS({ | ||
98 | + default: { | ||
99 | + container: { | ||
100 | + display: 'flex', | ||
101 | + flexDirection: 'row', | ||
102 | + alignItems: 'center' | ||
103 | + }, | ||
104 | + icon: { | ||
105 | + marginRight: '10px', | ||
106 | + marginBottom: 'auto', | ||
107 | + cursor: 'pointer', | ||
108 | + border: 'solid 1px rgba(0, 0, 0, .27)', | ||
109 | + borderRadius: '0' | ||
110 | + }, | ||
111 | + iconContainer: { | ||
112 | + display: 'flex', | ||
113 | + width: '100%' | ||
114 | + }, | ||
115 | + iconText: { | ||
116 | + width: '100%' | ||
117 | + }, | ||
118 | + }, | ||
119 | + }); | ||
120 | + | ||
121 | + let fieldClass = 'tb-field'; | ||
122 | + if (this.props.form.required) { | ||
123 | + fieldClass += ' tb-required'; | ||
124 | + } | ||
125 | + if (this.state.focused) { | ||
126 | + fieldClass += ' tb-focused'; | ||
127 | + } | ||
128 | + | ||
129 | + let pickedIcon = 'more_horiz'; | ||
130 | + let icon = ''; | ||
131 | + if (this.state.icon !== '') { | ||
132 | + pickedIcon = this.state.icon; | ||
133 | + icon = this.state.icon; | ||
134 | + } | ||
135 | + | ||
136 | + return ( | ||
137 | + <div style={ styles.container }> | ||
138 | + <div id='icon-container' style={ styles.iconContainer }> | ||
139 | + <IconButton style={ styles.icon }> | ||
140 | + <Icon>{pickedIcon}</Icon> | ||
141 | + </IconButton> | ||
142 | + <TextField | ||
143 | + className={fieldClass} | ||
144 | + label={this.props.form.title} | ||
145 | + error={!this.props.valid} | ||
146 | + helperText={this.props.valid ? this.props.form.placeholder : this.props.error} | ||
147 | + value={icon} | ||
148 | + disabled={this.props.form.readonly} | ||
149 | + onFocus={this.onFocus} | ||
150 | + onBlur={this.onBlur} | ||
151 | + style={ styles.iconText } /> | ||
152 | + </div> | ||
153 | + <Tooltip title='Clear' placement='top'><IconButton onClick={this.onClear}><ClearIcon/></IconButton></Tooltip> | ||
154 | + </div> | ||
155 | + ); | ||
156 | + } | ||
157 | +} | ||
158 | + | ||
159 | +export default ThingsboardBaseComponent(ThingsboardIcon); |
@@ -32,7 +32,8 @@ import ThingsboardImage from './json-form-image'; | @@ -32,7 +32,8 @@ import ThingsboardImage from './json-form-image'; | ||
32 | import ThingsboardCheckbox from './json-form-checkbox'; | 32 | import ThingsboardCheckbox from './json-form-checkbox'; |
33 | import ThingsboardHelp from './json-form-help'; | 33 | import ThingsboardHelp from './json-form-help'; |
34 | import ThingsboardFieldSet from './json-form-fieldset'; | 34 | import ThingsboardFieldSet from './json-form-fieldset'; |
35 | -import { JsonFormProps, JsonFormData, onChangeFn, OnColorClickFn } from './json-form.models'; | 35 | +import ThingsboardIcon from './json-form-icon'; |
36 | +import { JsonFormProps, JsonFormData, onChangeFn, OnColorClickFn, OnIconClickFn } from './json-form.models'; | ||
36 | 37 | ||
37 | import _ from 'lodash'; | 38 | import _ from 'lodash'; |
38 | import * as tinycolor_ from 'tinycolor2'; | 39 | import * as tinycolor_ from 'tinycolor2'; |
@@ -65,11 +66,13 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | @@ -65,11 +66,13 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | ||
65 | css: ThingsboardCss, | 66 | css: ThingsboardCss, |
66 | color: ThingsboardColor, | 67 | color: ThingsboardColor, |
67 | 'rc-select': ThingsboardRcSelect, | 68 | 'rc-select': ThingsboardRcSelect, |
68 | - fieldset: ThingsboardFieldSet | 69 | + fieldset: ThingsboardFieldSet, |
70 | + icon: ThingsboardIcon | ||
69 | }; | 71 | }; |
70 | 72 | ||
71 | this.onChange = this.onChange.bind(this); | 73 | this.onChange = this.onChange.bind(this); |
72 | this.onColorClick = this.onColorClick.bind(this); | 74 | this.onColorClick = this.onColorClick.bind(this); |
75 | + this.onIconClick = this.onIconClick.bind(this); | ||
73 | this.onToggleFullscreen = this.onToggleFullscreen.bind(this); | 76 | this.onToggleFullscreen = this.onToggleFullscreen.bind(this); |
74 | this.hasConditions = false; | 77 | this.hasConditions = false; |
75 | } | 78 | } |
@@ -86,6 +89,11 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | @@ -86,6 +89,11 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | ||
86 | this.props.onColorClick(key, val, colorSelectedFn); | 89 | this.props.onColorClick(key, val, colorSelectedFn); |
87 | } | 90 | } |
88 | 91 | ||
92 | + onIconClick(key: (string | number)[], val: string, | ||
93 | + iconSelectedFn: (icon: string) => void) { | ||
94 | + this.props.onIconClick(key, val, iconSelectedFn); | ||
95 | + } | ||
96 | + | ||
89 | onToggleFullscreen(element: HTMLElement, fullscreenFinishFn?: () => void) { | 97 | onToggleFullscreen(element: HTMLElement, fullscreenFinishFn?: () => void) { |
90 | this.props.onToggleFullscreen(element, fullscreenFinishFn); | 98 | this.props.onToggleFullscreen(element, fullscreenFinishFn); |
91 | } | 99 | } |
@@ -96,6 +104,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | @@ -96,6 +104,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | ||
96 | index: number, | 104 | index: number, |
97 | onChange: onChangeFn, | 105 | onChange: onChangeFn, |
98 | onColorClick: OnColorClickFn, | 106 | onColorClick: OnColorClickFn, |
107 | + onIconClick: OnIconClickFn, | ||
99 | onToggleFullscreen: () => void, | 108 | onToggleFullscreen: () => void, |
100 | mapper: {[type: string]: any}): JSX.Element { | 109 | mapper: {[type: string]: any}): JSX.Element { |
101 | const type = form.type; | 110 | const type = form.type; |
@@ -113,6 +122,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | @@ -113,6 +122,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | ||
113 | } | 122 | } |
114 | return <Field model={model} form={form} key={index} onChange={onChange} | 123 | return <Field model={model} form={form} key={index} onChange={onChange} |
115 | onColorClick={onColorClick} | 124 | onColorClick={onColorClick} |
125 | + onIconClick={onIconClick} | ||
116 | onToggleFullscreen={onToggleFullscreen} | 126 | onToggleFullscreen={onToggleFullscreen} |
117 | mapper={mapper} builder={this.builder}/>; | 127 | mapper={mapper} builder={this.builder}/>; |
118 | } | 128 | } |
@@ -124,7 +134,8 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | @@ -124,7 +134,8 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { | ||
124 | mapper = _.merge(this.mapper, this.props.mapper); | 134 | mapper = _.merge(this.mapper, this.props.mapper); |
125 | } | 135 | } |
126 | const forms = merged.map(function(form, index) { | 136 | const forms = merged.map(function(form, index) { |
127 | - return this.builder(form, this.props.model, index, this.onChange, this.onColorClick, this.onToggleFullscreen, mapper); | 137 | + return this.builder(form, this.props.model, index, this.onChange, this.onColorClick, |
138 | + this.onIconClick, this.onToggleFullscreen, mapper); | ||
128 | }.bind(this)); | 139 | }.bind(this)); |
129 | 140 | ||
130 | let formClass = 'SchemaForm'; | 141 | let formClass = 'SchemaForm'; |
@@ -49,6 +49,8 @@ export interface DefaultsFormOptions { | @@ -49,6 +49,8 @@ export interface DefaultsFormOptions { | ||
49 | export type onChangeFn = (key: (string | number)[], val: any, forceUpdate?: boolean) => void; | 49 | export type onChangeFn = (key: (string | number)[], val: any, forceUpdate?: boolean) => void; |
50 | export type OnColorClickFn = (key: (string | number)[], val: tinycolor.ColorFormats.RGBA, | 50 | export type OnColorClickFn = (key: (string | number)[], val: tinycolor.ColorFormats.RGBA, |
51 | colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) => void; | 51 | colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) => void; |
52 | +export type OnIconClickFn = (key: (string | number)[], val: string, | ||
53 | + iconSelectedFn: (icon: string) => void) => void; | ||
52 | export type onToggleFullscreenFn = (element: HTMLElement, fullscreenFinishFn?: () => void) => void; | 54 | export type onToggleFullscreenFn = (element: HTMLElement, fullscreenFinishFn?: () => void) => void; |
53 | 55 | ||
54 | export interface JsonFormProps { | 56 | export interface JsonFormProps { |
@@ -61,6 +63,7 @@ export interface JsonFormProps { | @@ -61,6 +63,7 @@ export interface JsonFormProps { | ||
61 | option: FormOption; | 63 | option: FormOption; |
62 | onModelChange?: onChangeFn; | 64 | onModelChange?: onChangeFn; |
63 | onColorClick?: OnColorClickFn; | 65 | onColorClick?: OnColorClickFn; |
66 | + onIconClick?: OnIconClickFn; | ||
64 | onToggleFullscreen?: onToggleFullscreenFn; | 67 | onToggleFullscreen?: onToggleFullscreenFn; |
65 | mapper?: {[type: string]: any}; | 68 | mapper?: {[type: string]: any}; |
66 | } | 69 | } |
@@ -107,6 +110,7 @@ export type ComponentBuilderFn = (form: JsonFormData, | @@ -107,6 +110,7 @@ export type ComponentBuilderFn = (form: JsonFormData, | ||
107 | index: number, | 110 | index: number, |
108 | onChange: onChangeFn, | 111 | onChange: onChangeFn, |
109 | onColorClick: OnColorClickFn, | 112 | onColorClick: OnColorClickFn, |
113 | + onIconClick: OnIconClickFn, | ||
110 | onToggleFullscreen: onToggleFullscreenFn, | 114 | onToggleFullscreen: onToggleFullscreenFn, |
111 | mapper: {[type: string]: any}) => JSX.Element; | 115 | mapper: {[type: string]: any}) => JSX.Element; |
112 | 116 | ||
@@ -118,6 +122,7 @@ export interface JsonFormFieldProps { | @@ -118,6 +122,7 @@ export interface JsonFormFieldProps { | ||
118 | mapper?: {[type: string]: any}; | 122 | mapper?: {[type: string]: any}; |
119 | onChange?: onChangeFn; | 123 | onChange?: onChangeFn; |
120 | onColorClick?: OnColorClickFn; | 124 | onColorClick?: OnColorClickFn; |
125 | + onIconClick?: OnIconClickFn; | ||
121 | onChangeValidate?: (e: any, forceUpdate?: boolean) => void; | 126 | onChangeValidate?: (e: any, forceUpdate?: boolean) => void; |
122 | onToggleFullscreen?: onToggleFullscreenFn; | 127 | onToggleFullscreen?: onToggleFullscreenFn; |
123 | valid?: boolean; | 128 | valid?: boolean; |
@@ -21,13 +21,13 @@ | @@ -21,13 +21,13 @@ | ||
21 | <mat-placeholder translate>datetime.date-from</mat-placeholder> | 21 | <mat-placeholder translate>datetime.date-from</mat-placeholder> |
22 | <mat-datetimepicker-toggle [for]="startDatePicker" matPrefix></mat-datetimepicker-toggle> | 22 | <mat-datetimepicker-toggle [for]="startDatePicker" matPrefix></mat-datetimepicker-toggle> |
23 | <mat-datetimepicker #startDatePicker type="date" openOnFocus="true"></mat-datetimepicker> | 23 | <mat-datetimepicker #startDatePicker type="date" openOnFocus="true"></mat-datetimepicker> |
24 | - <input matInput [(ngModel)]="startDate" [matDatetimepicker]="startDatePicker" (ngModelChange)="onStartDateChange()"> | 24 | + <input matInput [disabled]="disabled" [(ngModel)]="startDate" [matDatetimepicker]="startDatePicker" (ngModelChange)="onStartDateChange()"> |
25 | </mat-form-field> | 25 | </mat-form-field> |
26 | <mat-form-field> | 26 | <mat-form-field> |
27 | <mat-placeholder translate>datetime.time-from</mat-placeholder> | 27 | <mat-placeholder translate>datetime.time-from</mat-placeholder> |
28 | <mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle> | 28 | <mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle> |
29 | <mat-datetimepicker #startTimePicker type="time" openOnFocus="true"></mat-datetimepicker> | 29 | <mat-datetimepicker #startTimePicker type="time" openOnFocus="true"></mat-datetimepicker> |
30 | - <input matInput [(ngModel)]="startDate" [matDatetimepicker]="startTimePicker" (ngModelChange)="onStartDateChange()"> | 30 | + <input matInput [disabled]="disabled" [(ngModel)]="startDate" [matDatetimepicker]="startTimePicker" (ngModelChange)="onStartDateChange()"> |
31 | </mat-form-field> | 31 | </mat-form-field> |
32 | </section> | 32 | </section> |
33 | <section fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="16px"> | 33 | <section fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="16px"> |
@@ -35,13 +35,13 @@ | @@ -35,13 +35,13 @@ | ||
35 | <mat-placeholder translate>datetime.date-to</mat-placeholder> | 35 | <mat-placeholder translate>datetime.date-to</mat-placeholder> |
36 | <mat-datetimepicker-toggle [for]="endDatePicker" matPrefix></mat-datetimepicker-toggle> | 36 | <mat-datetimepicker-toggle [for]="endDatePicker" matPrefix></mat-datetimepicker-toggle> |
37 | <mat-datetimepicker #endDatePicker type="date" openOnFocus="true"></mat-datetimepicker> | 37 | <mat-datetimepicker #endDatePicker type="date" openOnFocus="true"></mat-datetimepicker> |
38 | - <input matInput [(ngModel)]="endDate" [matDatetimepicker]="endDatePicker" (ngModelChange)="onEndDateChange()"> | 38 | + <input matInput [disabled]="disabled" [(ngModel)]="endDate" [matDatetimepicker]="endDatePicker" (ngModelChange)="onEndDateChange()"> |
39 | </mat-form-field> | 39 | </mat-form-field> |
40 | <mat-form-field> | 40 | <mat-form-field> |
41 | <mat-placeholder translate>datetime.time-to</mat-placeholder> | 41 | <mat-placeholder translate>datetime.time-to</mat-placeholder> |
42 | <mat-datetimepicker-toggle [for]="endTimePicker" matPrefix></mat-datetimepicker-toggle> | 42 | <mat-datetimepicker-toggle [for]="endTimePicker" matPrefix></mat-datetimepicker-toggle> |
43 | <mat-datetimepicker #endTimePicker type="time" openOnFocus="true"></mat-datetimepicker> | 43 | <mat-datetimepicker #endTimePicker type="time" openOnFocus="true"></mat-datetimepicker> |
44 | - <input matInput [(ngModel)]="endDate" [matDatetimepicker]="endTimePicker" (ngModelChange)="onEndDateChange()"> | 44 | + <input matInput [disabled]="disabled" [(ngModel)]="endDate" [matDatetimepicker]="endTimePicker" (ngModelChange)="onEndDateChange()"> |
45 | </mat-form-field> | 45 | </mat-form-field> |
46 | </section> | 46 | </section> |
47 | </section> | 47 | </section> |
@@ -16,39 +16,43 @@ | @@ -16,39 +16,43 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <section fxLayout="row"> | 18 | <section fxLayout="row"> |
19 | - <section class="interval-section" fxLayout="column" fxFlex [fxShow]="advanced"> | 19 | + <section fxLayout="column" [fxShow]="isEdit"> |
20 | + <label class="tb-small hide-label" translate>timewindow.hide</label> | ||
21 | + <mat-checkbox [(ngModel)]="hideFlag" (ngModelChange)="onHideFlagChange()"></mat-checkbox> | ||
22 | + </section> | ||
23 | + <section class="interval-section" fxLayout="column" fxFlex [fxShow]="advanced && (isEdit || !hideFlag)"> | ||
20 | <label class="tb-small interval-label" translate>{{ predefinedName }}</label> | 24 | <label class="tb-small interval-label" translate>{{ predefinedName }}</label> |
21 | <section fxLayout="row" fxLayoutAlign="start start" fxFlex fxLayoutGap="6px"> | 25 | <section fxLayout="row" fxLayoutAlign="start start" fxFlex fxLayoutGap="6px"> |
22 | <mat-form-field class="number-input"> | 26 | <mat-form-field class="number-input"> |
23 | <mat-label translate>timeinterval.days</mat-label> | 27 | <mat-label translate>timeinterval.days</mat-label> |
24 | - <input matInput type="number" step="1" min="0" [(ngModel)]="days" (ngModelChange)="onTimeInputChange('days')"/> | 28 | + <input matInput [disabled]="hideFlag || disabled" type="number" step="1" min="0" [(ngModel)]="days" (ngModelChange)="onTimeInputChange('days')"/> |
25 | </mat-form-field> | 29 | </mat-form-field> |
26 | <mat-form-field class="number-input"> | 30 | <mat-form-field class="number-input"> |
27 | <mat-label translate>timeinterval.hours</mat-label> | 31 | <mat-label translate>timeinterval.hours</mat-label> |
28 | - <input matInput type="number" step="1" [(ngModel)]="hours" (ngModelChange)="onTimeInputChange('hours')"/> | 32 | + <input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="hours" (ngModelChange)="onTimeInputChange('hours')"/> |
29 | </mat-form-field> | 33 | </mat-form-field> |
30 | <mat-form-field class="number-input"> | 34 | <mat-form-field class="number-input"> |
31 | <mat-label translate>timeinterval.minutes</mat-label> | 35 | <mat-label translate>timeinterval.minutes</mat-label> |
32 | - <input matInput type="number" step="1" [(ngModel)]="mins" (ngModelChange)="onTimeInputChange('mins')"/> | 36 | + <input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="mins" (ngModelChange)="onTimeInputChange('mins')"/> |
33 | </mat-form-field> | 37 | </mat-form-field> |
34 | <mat-form-field class="number-input"> | 38 | <mat-form-field class="number-input"> |
35 | <mat-label translate>timeinterval.seconds</mat-label> | 39 | <mat-label translate>timeinterval.seconds</mat-label> |
36 | - <input matInput type="number" step="1" [(ngModel)]="secs" (ngModelChange)="onTimeInputChange('secs')"/> | 40 | + <input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="secs" (ngModelChange)="onTimeInputChange('secs')"/> |
37 | </mat-form-field> | 41 | </mat-form-field> |
38 | </section> | 42 | </section> |
39 | </section> | 43 | </section> |
40 | - <section class="interval-section" fxLayout="row" fxFlex [fxShow]="!advanced"> | 44 | + <section class="interval-section" fxLayout="row" fxFlex [fxShow]="!advanced && (isEdit || !hideFlag)"> |
41 | <mat-form-field fxFlex> | 45 | <mat-form-field fxFlex> |
42 | <mat-label translate>{{ predefinedName }}</mat-label> | 46 | <mat-label translate>{{ predefinedName }}</mat-label> |
43 | - <mat-select matInput [(ngModel)]="intervalMs" (ngModelChange)="onIntervalMsChange()" style="min-width: 150px;"> | 47 | + <mat-select matInput [disabled]="hideFlag || disabled" [(ngModel)]="intervalMs" (ngModelChange)="onIntervalMsChange()" style="min-width: 150px;"> |
44 | <mat-option *ngFor="let interval of intervals" [value]="interval.value"> | 48 | <mat-option *ngFor="let interval of intervals" [value]="interval.value"> |
45 | {{ interval.name | translate:interval.translateParams }} | 49 | {{ interval.name | translate:interval.translateParams }} |
46 | </mat-option> | 50 | </mat-option> |
47 | </mat-select> | 51 | </mat-select> |
48 | </mat-form-field> | 52 | </mat-form-field> |
49 | </section> | 53 | </section> |
50 | - <section fxLayout="column" fxLayoutAlign="center center"> | 54 | + <section fxLayout="column" fxLayoutAlign="center center" [fxShow]="(isEdit || !hideFlag)"> |
51 | <label class="tb-small advanced-label" translate>timeinterval.advanced</label> | 55 | <label class="tb-small advanced-label" translate>timeinterval.advanced</label> |
52 | - <mat-slide-toggle class="advanced-switch" [(ngModel)]="advanced" (ngModelChange)="onAdvancedChange()"></mat-slide-toggle> | 56 | + <mat-slide-toggle [disabled]="hideFlag || disabled" class="advanced-switch" [(ngModel)]="advanced" (ngModelChange)="onAdvancedChange()"></mat-slide-toggle> |
53 | </section> | 57 | </section> |
54 | </section> | 58 | </section> |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; | 17 | +import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; |
18 | import { | 18 | import { |
19 | ControlValueAccessor, | 19 | ControlValueAccessor, |
20 | FormControl, | 20 | FormControl, |
@@ -24,6 +24,7 @@ import { | @@ -24,6 +24,7 @@ import { | ||
24 | } from '@angular/forms'; | 24 | } from '@angular/forms'; |
25 | import { Timewindow } from '@shared/models/time/time.models'; | 25 | import { Timewindow } from '@shared/models/time/time.models'; |
26 | import { TimeInterval, TimeService } from '@core/services/time.service'; | 26 | import { TimeInterval, TimeService } from '@core/services/time.service'; |
27 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
27 | 28 | ||
28 | @Component({ | 29 | @Component({ |
29 | selector: 'tb-timeinterval', | 30 | selector: 'tb-timeinterval', |
@@ -61,6 +62,31 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { | @@ -61,6 +62,31 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { | ||
61 | } | 62 | } |
62 | 63 | ||
63 | @Input() predefinedName: string; | 64 | @Input() predefinedName: string; |
65 | + | ||
66 | + isEditValue = false; | ||
67 | + | ||
68 | + @Input() | ||
69 | + set isEdit(val) { | ||
70 | + this.isEditValue = coerceBooleanProperty(val); | ||
71 | + } | ||
72 | + | ||
73 | + get isEdit() { | ||
74 | + return this.isEditValue; | ||
75 | + } | ||
76 | + | ||
77 | + hideFlagValue = false; | ||
78 | + | ||
79 | + @Input() | ||
80 | + get hideFlag() { | ||
81 | + return this.hideFlagValue; | ||
82 | + } | ||
83 | + | ||
84 | + set hideFlag(val) { | ||
85 | + this.hideFlagValue = val; | ||
86 | + } | ||
87 | + | ||
88 | + @Output() hideFlagChange = new EventEmitter<boolean>(); | ||
89 | + | ||
64 | @Input() disabled: boolean; | 90 | @Input() disabled: boolean; |
65 | 91 | ||
66 | days = 0; | 92 | days = 0; |
@@ -189,6 +215,10 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { | @@ -189,6 +215,10 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { | ||
189 | this.updateView(); | 215 | this.updateView(); |
190 | } | 216 | } |
191 | 217 | ||
218 | + onHideFlagChange() { | ||
219 | + this.hideFlagChange.emit(this.hideFlagValue); | ||
220 | + } | ||
221 | + | ||
192 | onTimeInputChange(type: string) { | 222 | onTimeInputChange(type: string) { |
193 | switch (type) { | 223 | switch (type) { |
194 | case 'secs': | 224 | case 'secs': |
@@ -23,6 +23,9 @@ | @@ -23,6 +23,9 @@ | ||
23 | <mat-tab label="{{ 'timewindow.realtime' | translate }}"> | 23 | <mat-tab label="{{ 'timewindow.realtime' | translate }}"> |
24 | <div formGroupName="realtime" class="mat-content mat-padding" fxLayout="column"> | 24 | <div formGroupName="realtime" class="mat-content mat-padding" fxLayout="column"> |
25 | <tb-timeinterval | 25 | <tb-timeinterval |
26 | + [(hideFlag)]="timewindow.hideInterval" | ||
27 | + (hideFlagChange)="onHideIntervalChanged()" | ||
28 | + [isEdit]="isEdit" | ||
26 | formControlName="timewindowMs" | 29 | formControlName="timewindowMs" |
27 | predefinedName="timewindow.last" | 30 | predefinedName="timewindow.last" |
28 | [required]="timewindow.selectedTab === timewindowTypes.REALTIME" | 31 | [required]="timewindow.selectedTab === timewindowTypes.REALTIME" |
@@ -30,66 +33,95 @@ | @@ -30,66 +33,95 @@ | ||
30 | </div> | 33 | </div> |
31 | </mat-tab> | 34 | </mat-tab> |
32 | <mat-tab label="{{ 'timewindow.history' | translate }}"> | 35 | <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 && | 36 | + <section fxLayout="row"> |
37 | + <section *ngIf="isEdit" fxLayout="column" style="padding-top: 8px; padding-left: 16px;"> | ||
38 | + <label class="tb-small hide-label" translate>timewindow.hide</label> | ||
39 | + <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideInterval" | ||
40 | + (ngModelChange)="onHideIntervalChanged()"></mat-checkbox> | ||
41 | + </section> | ||
42 | + <section fxLayout="column" [fxShow]="isEdit || !timewindow.hideInterval"> | ||
43 | + <div formGroupName="history" class="mat-content mat-padding" style="padding-top: 8px;"> | ||
44 | + <mat-radio-group formControlName="historyType"> | ||
45 | + <mat-radio-button [value]="historyTypes.LAST_INTERVAL" color="primary"> | ||
46 | + <section fxLayout="column"> | ||
47 | + <tb-timeinterval | ||
48 | + formControlName="timewindowMs" | ||
49 | + predefinedName="timewindow.last" | ||
50 | + [fxShow]="timewindowForm.get('history').get('historyType').value === historyTypes.LAST_INTERVAL" | ||
51 | + [required]="timewindow.selectedTab === timewindowTypes.HISTORY && | ||
42 | timewindowForm.get('history').get('historyType').value === historyTypes.LAST_INTERVAL" | 52 | 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 | + style="padding-top: 8px;"></tb-timeinterval> |
54 | + </section> | ||
55 | + </mat-radio-button> | ||
56 | + <mat-radio-button [value]="historyTypes.FIXED" color="primary"> | ||
57 | + <section fxLayout="column"> | ||
58 | + <span translate>timewindow.time-period</span> | ||
59 | + <tb-datetime-period | ||
60 | + formControlName="fixedTimewindow" | ||
61 | + [fxShow]="timewindowForm.get('history').get('historyType').value === historyTypes.FIXED" | ||
62 | + [required]="timewindow.selectedTab === timewindowTypes.HISTORY && | ||
53 | timewindowForm.get('history').get('historyType').value === historyTypes.FIXED" | 63 | 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> | 64 | + style="padding-top: 8px;"></tb-datetime-period> |
65 | + </section> | ||
66 | + </mat-radio-button> | ||
67 | + </mat-radio-group> | ||
68 | + </div> | ||
69 | + </section> | ||
70 | + </section> | ||
59 | </mat-tab> | 71 | </mat-tab> |
60 | </mat-tab-group> | 72 | </mat-tab-group> |
61 | <div *ngIf="aggregation" formGroupName="aggregation" class="mat-content mat-padding" fxLayout="column"> | 73 | <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(aggregationTypes[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> | ||
86 | - </div> | 74 | + <section fxLayout="row"> |
75 | + <section fxLayout="column" [fxShow]="isEdit"> | ||
76 | + <label class="tb-small hide-label" translate>timewindow.hide</label> | ||
77 | + <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideAggregation" | ||
78 | + (ngModelChange)="onHideAggregationChanged()"></mat-checkbox> | ||
79 | + </section> | ||
80 | + <section fxFlex fxLayout="column" [fxShow]="isEdit || !timewindow.hideAggregation"> | ||
81 | + <mat-form-field> | ||
82 | + <mat-label translate>aggregation.function</mat-label> | ||
83 | + <mat-select matInput formControlName="type" style="min-width: 150px;"> | ||
84 | + <mat-option *ngFor="let aggregation of aggregations" [value]="aggregation"> | ||
85 | + {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }} | ||
86 | + </mat-option> | ||
87 | + </mat-select> | ||
88 | + </mat-form-field> | ||
89 | + </section> | ||
90 | + </section> | ||
91 | + <section fxLayout="row" [fxShow]="timewindowForm.get('aggregation').get('type').value === aggregationTypes.NONE"> | ||
92 | + <section fxLayout="column" [fxShow]="isEdit"> | ||
93 | + <label class="tb-small hide-label" translate>timewindow.hide</label> | ||
94 | + <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideAggInterval" | ||
95 | + (ngModelChange)="onHideAggIntervalChanged()"></mat-checkbox> | ||
96 | + </section> | ||
97 | + <section fxLayout="column" [fxShow]="isEdit || !timewindow.hideAggInterval"> | ||
98 | + <div class="limit-slider-container" | ||
99 | + fxLayout="row" fxLayoutAlign="start center"> | ||
100 | + <span translate>aggregation.limit</span> | ||
101 | + <mat-slider fxFlex formControlName="limit" | ||
102 | + thumbLabel | ||
103 | + [value]="timewindowForm.get('aggregation').get('limit').value" | ||
104 | + min="{{minDatapointsLimit()}}" | ||
105 | + max="{{maxDatapointsLimit()}}"> | ||
106 | + </mat-slider> | ||
107 | + <mat-form-field style="max-width: 80px;"> | ||
108 | + <input matInput formControlName="limit" type="number" step="1" | ||
109 | + [value]="timewindowForm.get('aggregation').get('limit').value" | ||
110 | + min="{{minDatapointsLimit()}}" | ||
111 | + max="{{maxDatapointsLimit()}}"/> | ||
112 | + </mat-form-field> | ||
113 | + </div> | ||
114 | + </section> | ||
115 | + </section> | ||
87 | </div> | 116 | </div> |
88 | <div formGroupName="realtime" | 117 | <div formGroupName="realtime" |
89 | *ngIf="aggregation && timewindowForm.get('aggregation').get('type').value !== aggregationTypes.NONE && | 118 | *ngIf="aggregation && timewindowForm.get('aggregation').get('type').value !== aggregationTypes.NONE && |
90 | timewindow.selectedTab === timewindowTypes.REALTIME" class="mat-content mat-padding" fxLayout="column"> | 119 | timewindow.selectedTab === timewindowTypes.REALTIME" class="mat-content mat-padding" fxLayout="column"> |
91 | <tb-timeinterval | 120 | <tb-timeinterval |
92 | formControlName="interval" | 121 | formControlName="interval" |
122 | + [isEdit]="isEdit" | ||
123 | + [(hideFlag)]="timewindow.hideAggInterval" | ||
124 | + (hideFlagChange)="onHideAggIntervalChanged()" | ||
93 | [min]="minRealtimeAggInterval()" [max]="maxRealtimeAggInterval()" | 125 | [min]="minRealtimeAggInterval()" [max]="maxRealtimeAggInterval()" |
94 | predefinedName="aggregation.group-interval"> | 126 | predefinedName="aggregation.group-interval"> |
95 | </tb-timeinterval> | 127 | </tb-timeinterval> |
@@ -99,6 +131,9 @@ | @@ -99,6 +131,9 @@ | ||
99 | timewindow.selectedTab === timewindowTypes.HISTORY" class="mat-content mat-padding" fxLayout="column"> | 131 | timewindow.selectedTab === timewindowTypes.HISTORY" class="mat-content mat-padding" fxLayout="column"> |
100 | <tb-timeinterval | 132 | <tb-timeinterval |
101 | formControlName="interval" | 133 | formControlName="interval" |
134 | + [isEdit]="isEdit" | ||
135 | + [(hideFlag)]="timewindow.hideAggInterval" | ||
136 | + (hideFlagChange)="onHideAggIntervalChanged()" | ||
102 | [min]="minHistoryAggInterval()" [max]="maxHistoryAggInterval()" | 137 | [min]="minHistoryAggInterval()" [max]="maxHistoryAggInterval()" |
103 | predefinedName="aggregation.group-interval"> | 138 | predefinedName="aggregation.group-interval"> |
104 | </tb-timeinterval> | 139 | </tb-timeinterval> |
@@ -30,6 +30,11 @@ | @@ -30,6 +30,11 @@ | ||
30 | padding: 0 16px; | 30 | padding: 0 16px; |
31 | } | 31 | } |
32 | 32 | ||
33 | + .hide-label { | ||
34 | + margin-bottom: 5px; | ||
35 | + margin-right: 5px; | ||
36 | + } | ||
37 | + | ||
33 | .limit-slider-container { | 38 | .limit-slider-container { |
34 | >:first-child { | 39 | >:first-child { |
35 | margin-right: 16px; | 40 | margin-right: 16px; |
@@ -48,6 +48,7 @@ export interface TimewindowPanelData { | @@ -48,6 +48,7 @@ export interface TimewindowPanelData { | ||
48 | historyOnly: boolean; | 48 | historyOnly: boolean; |
49 | timewindow: Timewindow; | 49 | timewindow: Timewindow; |
50 | aggregation: boolean; | 50 | aggregation: boolean; |
51 | + isEdit: boolean; | ||
51 | } | 52 | } |
52 | 53 | ||
53 | @Component({ | 54 | @Component({ |
@@ -61,6 +62,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | @@ -61,6 +62,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | ||
61 | 62 | ||
62 | aggregation = false; | 63 | aggregation = false; |
63 | 64 | ||
65 | + isEdit = false; | ||
66 | + | ||
64 | timewindow: Timewindow; | 67 | timewindow: Timewindow; |
65 | 68 | ||
66 | result: Timewindow; | 69 | result: Timewindow; |
@@ -82,18 +85,19 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | @@ -82,18 +85,19 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | ||
82 | protected store: Store<AppState>, | 85 | protected store: Store<AppState>, |
83 | public fb: FormBuilder, | 86 | public fb: FormBuilder, |
84 | private timeService: TimeService, | 87 | private timeService: TimeService, |
85 | - private translate: TranslateService, | ||
86 | - private millisecondsToTimeStringPipe: MillisecondsToTimeStringPipe, | ||
87 | - private datePipe: DatePipe, | ||
88 | - private overlay: Overlay, | ||
89 | public viewContainerRef: ViewContainerRef) { | 88 | public viewContainerRef: ViewContainerRef) { |
90 | super(store); | 89 | super(store); |
91 | this.historyOnly = data.historyOnly; | 90 | this.historyOnly = data.historyOnly; |
92 | this.timewindow = data.timewindow; | 91 | this.timewindow = data.timewindow; |
93 | this.aggregation = data.aggregation; | 92 | this.aggregation = data.aggregation; |
93 | + this.isEdit = data.isEdit; | ||
94 | } | 94 | } |
95 | 95 | ||
96 | ngOnInit(): void { | 96 | ngOnInit(): void { |
97 | + const hideInterval = this.timewindow.hideInterval || false; | ||
98 | + const hideAggregation = this.timewindow.hideAggregation || false; | ||
99 | + const hideAggInterval = this.timewindow.hideAggInterval || false; | ||
100 | + | ||
97 | this.timewindowForm = this.fb.group({ | 101 | this.timewindowForm = this.fb.group({ |
98 | realtime: this.fb.group( | 102 | realtime: this.fb.group( |
99 | { | 103 | { |
@@ -109,42 +113,46 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | @@ -109,42 +113,46 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | ||
109 | ), | 113 | ), |
110 | history: this.fb.group( | 114 | history: this.fb.group( |
111 | { | 115 | { |
112 | - historyType: [ | ||
113 | - this.timewindow.history && typeof this.timewindow.history.historyType !== 'undefined' | ||
114 | - ? this.timewindow.history.historyType : HistoryWindowType.LAST_INTERVAL | ||
115 | - ], | ||
116 | - timewindowMs: [ | ||
117 | - this.timewindow.history && typeof this.timewindow.history.timewindowMs !== 'undefined' | ||
118 | - ? this.timewindow.history.timewindowMs : null | ||
119 | - ], | 116 | + historyType: this.fb.control({ |
117 | + value: this.timewindow.history && typeof this.timewindow.history.historyType !== 'undefined' | ||
118 | + ? this.timewindow.history.historyType : HistoryWindowType.LAST_INTERVAL, | ||
119 | + disabled: hideInterval | ||
120 | + }), | ||
121 | + timewindowMs: this.fb.control({ | ||
122 | + value: this.timewindow.history && typeof this.timewindow.history.timewindowMs !== 'undefined' | ||
123 | + ? this.timewindow.history.timewindowMs : null, | ||
124 | + disabled: hideInterval | ||
125 | + }), | ||
120 | interval: [ | 126 | interval: [ |
121 | this.timewindow.history && typeof this.timewindow.history.interval !== 'undefined' | 127 | this.timewindow.history && typeof this.timewindow.history.interval !== 'undefined' |
122 | ? this.timewindow.history.interval : null | 128 | ? this.timewindow.history.interval : null |
123 | ], | 129 | ], |
124 | - fixedTimewindow: [ | ||
125 | - this.timewindow.history && typeof this.timewindow.history.fixedTimewindow !== 'undefined' | ||
126 | - ? this.timewindow.history.fixedTimewindow : null | ||
127 | - ] | 130 | + fixedTimewindow: this.fb.control({ |
131 | + value: this.timewindow.history && typeof this.timewindow.history.fixedTimewindow !== 'undefined' | ||
132 | + ? this.timewindow.history.fixedTimewindow : null, | ||
133 | + disabled: hideInterval | ||
134 | + }) | ||
128 | } | 135 | } |
129 | ), | 136 | ), |
130 | aggregation: this.fb.group( | 137 | aggregation: this.fb.group( |
131 | { | 138 | { |
132 | - type: [ | ||
133 | - this.timewindow.aggregation && typeof this.timewindow.aggregation.type !== 'undefined' | ||
134 | - ? this.timewindow.aggregation.type : null | ||
135 | - ], | ||
136 | - limit: [ | ||
137 | - this.timewindow.aggregation && typeof this.timewindow.aggregation.limit !== 'undefined' | 139 | + type: this.fb.control({ |
140 | + value: this.timewindow.aggregation && typeof this.timewindow.aggregation.type !== 'undefined' | ||
141 | + ? this.timewindow.aggregation.type : null, | ||
142 | + disabled: hideAggregation | ||
143 | + }), | ||
144 | + limit: this.fb.control({ | ||
145 | + value: this.timewindow.aggregation && typeof this.timewindow.aggregation.limit !== 'undefined' | ||
138 | ? this.timewindow.aggregation.limit : null, | 146 | ? this.timewindow.aggregation.limit : null, |
139 | - [Validators.min(this.minDatapointsLimit()), Validators.max(this.maxDatapointsLimit())] | ||
140 | - ] | 147 | + disabled: hideAggInterval |
148 | + }, [Validators.min(this.minDatapointsLimit()), Validators.max(this.maxDatapointsLimit())]) | ||
141 | } | 149 | } |
142 | ) | 150 | ) |
143 | }); | 151 | }); |
144 | } | 152 | } |
145 | 153 | ||
146 | update() { | 154 | update() { |
147 | - const timewindowFormValue = this.timewindowForm.value; | 155 | + const timewindowFormValue = this.timewindowForm.getRawValue(); |
148 | this.timewindow.realtime = { | 156 | this.timewindow.realtime = { |
149 | timewindowMs: timewindowFormValue.realtime.timewindowMs, | 157 | timewindowMs: timewindowFormValue.realtime.timewindowMs, |
150 | interval: timewindowFormValue.realtime.interval | 158 | interval: timewindowFormValue.realtime.interval |
@@ -194,7 +202,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | @@ -194,7 +202,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | ||
194 | } | 202 | } |
195 | 203 | ||
196 | currentHistoryTimewindow() { | 204 | currentHistoryTimewindow() { |
197 | - const timewindowFormValue = this.timewindowForm.value; | 205 | + const timewindowFormValue = this.timewindowForm.getRawValue(); |
198 | if (timewindowFormValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { | 206 | if (timewindowFormValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { |
199 | return timewindowFormValue.history.timewindowMs; | 207 | return timewindowFormValue.history.timewindowMs; |
200 | } else { | 208 | } else { |
@@ -203,4 +211,35 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | @@ -203,4 +211,35 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { | ||
203 | } | 211 | } |
204 | } | 212 | } |
205 | 213 | ||
214 | + onHideIntervalChanged() { | ||
215 | + if (this.timewindow.hideInterval) { | ||
216 | + this.timewindowForm.get('history').get('historyType').disable({emitEvent: false}); | ||
217 | + this.timewindowForm.get('history').get('timewindowMs').disable({emitEvent: false}); | ||
218 | + this.timewindowForm.get('history').get('fixedTimewindow').disable({emitEvent: false}); | ||
219 | + } else { | ||
220 | + this.timewindowForm.get('history').get('historyType').enable({emitEvent: false}); | ||
221 | + this.timewindowForm.get('history').get('timewindowMs').enable({emitEvent: false}); | ||
222 | + this.timewindowForm.get('history').get('fixedTimewindow').enable({emitEvent: false}); | ||
223 | + } | ||
224 | + this.timewindowForm.markAsDirty(); | ||
225 | + } | ||
226 | + | ||
227 | + onHideAggregationChanged() { | ||
228 | + if (this.timewindow.hideAggregation) { | ||
229 | + this.timewindowForm.get('aggregation').get('type').disable({emitEvent: false}); | ||
230 | + } else { | ||
231 | + this.timewindowForm.get('aggregation').get('type').enable({emitEvent: false}); | ||
232 | + } | ||
233 | + this.timewindowForm.markAsDirty(); | ||
234 | + } | ||
235 | + | ||
236 | + onHideAggIntervalChanged() { | ||
237 | + if (this.timewindow.hideAggInterval) { | ||
238 | + this.timewindowForm.get('aggregation').get('limit').disable({emitEvent: false}); | ||
239 | + } else { | ||
240 | + this.timewindowForm.get('aggregation').get('limit').enable({emitEvent: false}); | ||
241 | + } | ||
242 | + this.timewindowForm.markAsDirty(); | ||
243 | + } | ||
244 | + | ||
206 | } | 245 | } |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<button *ngIf="asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" [disabled]="disabled" | 18 | +<button *ngIf="asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" [disabled]="timewindowDisabled" |
19 | type="button" | 19 | type="button" |
20 | mat-raised-button color="primary" (click)="openEditMode()"> | 20 | mat-raised-button color="primary" (click)="openEditMode()"> |
21 | <mat-icon class="material-icons">query_builder</mat-icon> | 21 | <mat-icon class="material-icons">query_builder</mat-icon> |
@@ -23,7 +23,7 @@ | @@ -23,7 +23,7 @@ | ||
23 | </button> | 23 | </button> |
24 | <section *ngIf="!asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" | 24 | <section *ngIf="!asButton" cdkOverlayOrigin #timewindowPanelOrigin="cdkOverlayOrigin" |
25 | class="tb-timewindow" fxLayout="row" fxLayoutAlign="start center"> | 25 | class="tb-timewindow" fxLayout="row" fxLayoutAlign="start center"> |
26 | - <button *ngIf="direction === 'left'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32" | 26 | + <button *ngIf="direction === 'left'" [disabled]="timewindowDisabled" mat-button mat-icon-button class="tb-mat-32" |
27 | type="button" | 27 | type="button" |
28 | (click)="openEditMode()" | 28 | (click)="openEditMode()" |
29 | matTooltip="{{ 'timewindow.edit' | translate }}" | 29 | matTooltip="{{ 'timewindow.edit' | translate }}" |
@@ -36,7 +36,7 @@ | @@ -36,7 +36,7 @@ | ||
36 | [matTooltipPosition]="tooltipPosition"> | 36 | [matTooltipPosition]="tooltipPosition"> |
37 | {{innerValue?.displayValue}} | 37 | {{innerValue?.displayValue}} |
38 | </span> | 38 | </span> |
39 | - <button *ngIf="direction === 'right'" [disabled]="disabled" mat-button mat-icon-button class="tb-mat-32" | 39 | + <button *ngIf="direction === 'right'" [disabled]="timewindowDisabled" mat-button mat-icon-button class="tb-mat-32" |
40 | type="button" | 40 | type="button" |
41 | (click)="openEditMode()" | 41 | (click)="openEditMode()" |
42 | matTooltip="{{ 'timewindow.edit' | translate }}" | 42 | matTooltip="{{ 'timewindow.edit' | translate }}" |
@@ -53,6 +53,7 @@ import { WINDOW } from '@core/services/window.service'; | @@ -53,6 +53,7 @@ import { WINDOW } from '@core/services/window.service'; | ||
53 | import { TimeService } from '@core/services/time.service'; | 53 | import { TimeService } from '@core/services/time.service'; |
54 | import { TooltipPosition } from '@angular/material/tooltip'; | 54 | import { TooltipPosition } from '@angular/material/tooltip'; |
55 | import { deepClone } from '@core/utils'; | 55 | import { deepClone } from '@core/utils'; |
56 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
56 | 57 | ||
57 | // @dynamic | 58 | // @dynamic |
58 | @Component({ | 59 | @Component({ |
@@ -73,7 +74,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -73,7 +74,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
73 | 74 | ||
74 | @Input() | 75 | @Input() |
75 | set historyOnly(val) { | 76 | set historyOnly(val) { |
76 | - this.historyOnlyValue = true; | 77 | + this.historyOnlyValue = coerceBooleanProperty(val); |
77 | } | 78 | } |
78 | 79 | ||
79 | get historyOnly() { | 80 | get historyOnly() { |
@@ -84,7 +85,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -84,7 +85,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
84 | 85 | ||
85 | @Input() | 86 | @Input() |
86 | set aggregation(val) { | 87 | set aggregation(val) { |
87 | - this.aggregationValue = true; | 88 | + this.aggregationValue = coerceBooleanProperty(val); |
88 | } | 89 | } |
89 | 90 | ||
90 | get aggregation() { | 91 | get aggregation() { |
@@ -95,7 +96,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -95,7 +96,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
95 | 96 | ||
96 | @Input() | 97 | @Input() |
97 | set isToolbar(val) { | 98 | set isToolbar(val) { |
98 | - this.isToolbarValue = true; | 99 | + this.isToolbarValue = coerceBooleanProperty(val); |
99 | } | 100 | } |
100 | 101 | ||
101 | get isToolbar() { | 102 | get isToolbar() { |
@@ -106,13 +107,25 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -106,13 +107,25 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
106 | 107 | ||
107 | @Input() | 108 | @Input() |
108 | set asButton(val) { | 109 | set asButton(val) { |
109 | - this.asButtonValue = true; | 110 | + this.asButtonValue = coerceBooleanProperty(val); |
110 | } | 111 | } |
111 | 112 | ||
112 | get asButton() { | 113 | get asButton() { |
113 | return this.asButtonValue; | 114 | return this.asButtonValue; |
114 | } | 115 | } |
115 | 116 | ||
117 | + isEditValue = false; | ||
118 | + | ||
119 | + @Input() | ||
120 | + set isEdit(val) { | ||
121 | + this.isEditValue = coerceBooleanProperty(val); | ||
122 | + this.timewindowDisabled = this.isTimewindowDisabled(); | ||
123 | + } | ||
124 | + | ||
125 | + get isEdit() { | ||
126 | + return this.isEditValue; | ||
127 | + } | ||
128 | + | ||
116 | @Input() | 129 | @Input() |
117 | direction: 'left' | 'right' = 'left'; | 130 | direction: 'left' | 'right' = 'left'; |
118 | 131 | ||
@@ -125,6 +138,8 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -125,6 +138,8 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
125 | 138 | ||
126 | innerValue: Timewindow; | 139 | innerValue: Timewindow; |
127 | 140 | ||
141 | + timewindowDisabled: boolean; | ||
142 | + | ||
128 | private propagateChange = (_: any) => {}; | 143 | private propagateChange = (_: any) => {}; |
129 | 144 | ||
130 | constructor(private translate: TranslateService, | 145 | constructor(private translate: TranslateService, |
@@ -145,7 +160,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -145,7 +160,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
145 | } | 160 | } |
146 | 161 | ||
147 | openEditMode() { | 162 | openEditMode() { |
148 | - if (this.disabled) { | 163 | + if (this.timewindowDisabled) { |
149 | return; | 164 | return; |
150 | } | 165 | } |
151 | const isGtSm = this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']); | 166 | const isGtSm = this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']); |
@@ -212,7 +227,8 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -212,7 +227,8 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
212 | { | 227 | { |
213 | timewindow: deepClone(this.innerValue), | 228 | timewindow: deepClone(this.innerValue), |
214 | historyOnly: this.historyOnly, | 229 | historyOnly: this.historyOnly, |
215 | - aggregation: this.aggregation | 230 | + aggregation: this.aggregation, |
231 | + isEdit: this.isEdit | ||
216 | } | 232 | } |
217 | ); | 233 | ); |
218 | 234 | ||
@@ -220,6 +236,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -220,6 +236,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
220 | componentRef.onDestroy(() => { | 236 | componentRef.onDestroy(() => { |
221 | if (componentRef.instance.result) { | 237 | if (componentRef.instance.result) { |
222 | this.innerValue = componentRef.instance.result; | 238 | this.innerValue = componentRef.instance.result; |
239 | + this.timewindowDisabled = this.isTimewindowDisabled(); | ||
223 | this.updateDisplayValue(); | 240 | this.updateDisplayValue(); |
224 | this.notifyChanged(); | 241 | this.notifyChanged(); |
225 | } | 242 | } |
@@ -243,10 +260,12 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -243,10 +260,12 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
243 | 260 | ||
244 | setDisabledState(isDisabled: boolean): void { | 261 | setDisabledState(isDisabled: boolean): void { |
245 | this.disabled = isDisabled; | 262 | this.disabled = isDisabled; |
263 | + this.timewindowDisabled = this.isTimewindowDisabled(); | ||
246 | } | 264 | } |
247 | 265 | ||
248 | writeValue(obj: Timewindow): void { | 266 | writeValue(obj: Timewindow): void { |
249 | this.innerValue = initModelFromDefaultTimewindow(obj, this.timeService); | 267 | this.innerValue = initModelFromDefaultTimewindow(obj, this.timeService); |
268 | + this.timewindowDisabled = this.isTimewindowDisabled(); | ||
250 | this.updateDisplayValue(); | 269 | this.updateDisplayValue(); |
251 | } | 270 | } |
252 | 271 | ||
@@ -276,4 +295,10 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | @@ -276,4 +295,10 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces | ||
276 | return this.isToolbar && !this.breakpointObserver.isMatched(MediaBreakpoints['gt-md']); | 295 | return this.isToolbar && !this.breakpointObserver.isMatched(MediaBreakpoints['gt-md']); |
277 | } | 296 | } |
278 | 297 | ||
298 | + private isTimewindowDisabled(): boolean { | ||
299 | + return this.disabled || | ||
300 | + (!this.isEdit && (!this.innerValue || this.innerValue.hideInterval && | ||
301 | + (!this.aggregation || this.innerValue.hideAggregation && this.innerValue.hideAggInterval))); | ||
302 | + } | ||
303 | + | ||
279 | } | 304 | } |
@@ -79,6 +79,9 @@ export interface Aggregation { | @@ -79,6 +79,9 @@ export interface Aggregation { | ||
79 | 79 | ||
80 | export interface Timewindow { | 80 | export interface Timewindow { |
81 | displayValue?: string; | 81 | displayValue?: string; |
82 | + hideInterval?: boolean; | ||
83 | + hideAggregation?: boolean; | ||
84 | + hideAggInterval?: boolean; | ||
82 | selectedTab?: TimewindowType; | 85 | selectedTab?: TimewindowType; |
83 | realtime?: IntervalWindow; | 86 | realtime?: IntervalWindow; |
84 | history?: HistoryWindow; | 87 | history?: HistoryWindow; |
@@ -115,9 +118,12 @@ export function historyInterval(timewindowMs: number): Timewindow { | @@ -115,9 +118,12 @@ export function historyInterval(timewindowMs: number): Timewindow { | ||
115 | } | 118 | } |
116 | 119 | ||
117 | export function defaultTimewindow(timeService: TimeService): Timewindow { | 120 | export function defaultTimewindow(timeService: TimeService): Timewindow { |
118 | - const currentTime = Date.now(); | 121 | + const currentTime = moment().valueOf(); |
119 | const timewindow: Timewindow = { | 122 | const timewindow: Timewindow = { |
120 | displayValue: '', | 123 | displayValue: '', |
124 | + hideInterval: false, | ||
125 | + hideAggregation: false, | ||
126 | + hideAggInterval: false, | ||
121 | selectedTab: TimewindowType.REALTIME, | 127 | selectedTab: TimewindowType.REALTIME, |
122 | realtime: { | 128 | realtime: { |
123 | interval: SECOND, | 129 | interval: SECOND, |
@@ -143,6 +149,9 @@ export function defaultTimewindow(timeService: TimeService): Timewindow { | @@ -143,6 +149,9 @@ export function defaultTimewindow(timeService: TimeService): Timewindow { | ||
143 | export function initModelFromDefaultTimewindow(value: Timewindow, timeService: TimeService): Timewindow { | 149 | export function initModelFromDefaultTimewindow(value: Timewindow, timeService: TimeService): Timewindow { |
144 | const model = defaultTimewindow(timeService); | 150 | const model = defaultTimewindow(timeService); |
145 | if (value) { | 151 | if (value) { |
152 | + model.hideInterval = value.hideInterval; | ||
153 | + model.hideAggregation = value.hideAggregation; | ||
154 | + model.hideAggInterval = value.hideAggInterval; | ||
146 | if (isUndefined(value.selectedTab)) { | 155 | if (isUndefined(value.selectedTab)) { |
147 | if (value.realtime) { | 156 | if (value.realtime) { |
148 | model.selectedTab = TimewindowType.REALTIME; | 157 | model.selectedTab = TimewindowType.REALTIME; |
@@ -206,6 +215,9 @@ export function toHistoryTimewindow(timewindow: Timewindow, startTimeMs: number, | @@ -206,6 +215,9 @@ export function toHistoryTimewindow(timewindow: Timewindow, startTimeMs: number, | ||
206 | limit = timeService.getMaxDatapointsLimit(); | 215 | limit = timeService.getMaxDatapointsLimit(); |
207 | } | 216 | } |
208 | const historyTimewindow: Timewindow = { | 217 | const historyTimewindow: Timewindow = { |
218 | + hideInterval: timewindow.hideInterval || false, | ||
219 | + hideAggregation: timewindow.hideAggregation || false, | ||
220 | + hideAggInterval: timewindow.hideAggInterval || false, | ||
209 | selectedTab: TimewindowType.HISTORY, | 221 | selectedTab: TimewindowType.HISTORY, |
210 | history: { | 222 | history: { |
211 | historyType: HistoryWindowType.FIXED, | 223 | historyType: HistoryWindowType.FIXED, |
@@ -319,6 +331,9 @@ export function createTimewindowForComparison(subscriptionTimewindow: Subscripti | @@ -319,6 +331,9 @@ export function createTimewindowForComparison(subscriptionTimewindow: Subscripti | ||
319 | 331 | ||
320 | export function cloneSelectedTimewindow(timewindow: Timewindow): Timewindow { | 332 | export function cloneSelectedTimewindow(timewindow: Timewindow): Timewindow { |
321 | const cloned: Timewindow = {}; | 333 | const cloned: Timewindow = {}; |
334 | + cloned.hideInterval = timewindow.hideInterval || false; | ||
335 | + cloned.hideAggregation = timewindow.hideAggregation || false; | ||
336 | + cloned.hideAggInterval = timewindow.hideAggInterval || false; | ||
322 | if (isDefined(timewindow.selectedTab)) { | 337 | if (isDefined(timewindow.selectedTab)) { |
323 | cloned.selectedTab = timewindow.selectedTab; | 338 | cloned.selectedTab = timewindow.selectedTab; |
324 | if (timewindow.selectedTab === TimewindowType.REALTIME) { | 339 | if (timewindow.selectedTab === TimewindowType.REALTIME) { |
@@ -345,6 +345,7 @@ export interface WidgetConfig { | @@ -345,6 +345,7 @@ export interface WidgetConfig { | ||
345 | showTitleIcon?: boolean; | 345 | showTitleIcon?: boolean; |
346 | iconColor?: string; | 346 | iconColor?: string; |
347 | iconSize?: string; | 347 | iconSize?: string; |
348 | + titleTooltip?: string; | ||
348 | dropShadow?: boolean; | 349 | dropShadow?: boolean; |
349 | enableFullscreen?: boolean; | 350 | enableFullscreen?: boolean; |
350 | useDashboardTimewindow?: boolean; | 351 | useDashboardTimewindow?: boolean; |