Commit a1225636198de80f243c47b5de5ef6a73e25a1e5

Authored by Igor Kulikov
1 parent 19dac7d5

Propagate UI changes

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>
@@ -24,6 +24,11 @@ @@ -24,6 +24,11 @@
24 margin: 5px 0; 24 margin: 5px 0;
25 } 25 }
26 26
  27 + .hide-label {
  28 + margin-bottom: 5px;
  29 + margin-right: 5px;
  30 + }
  31 +
27 .interval-section { 32 .interval-section {
28 min-height: 66px; 33 min-height: 66px;
29 .interval-label { 34 .interval-label {
@@ -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;