UI: Implement time series comparison widget settings.
This commit is contained in:
parent
95ac620977
commit
82ab52889b
@ -110,6 +110,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
TimeSeriesChartBasicConfigComponent
|
TimeSeriesChartBasicConfigComponent
|
||||||
} from '@home/components/widget/config/basic/chart/time-series-chart-basic-config.component';
|
} from '@home/components/widget/config/basic/chart/time-series-chart-basic-config.component';
|
||||||
|
import { ComparisonKeyRowComponent } from '@home/components/widget/config/basic/chart/comparison-key-row.component';
|
||||||
|
import {
|
||||||
|
ComparisonKeysTableComponent
|
||||||
|
} from '@home/components/widget/config/basic/chart/comparison-keys-table.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -145,7 +149,9 @@ import {
|
|||||||
PowerButtonBasicConfigComponent,
|
PowerButtonBasicConfigComponent,
|
||||||
SliderBasicConfigComponent,
|
SliderBasicConfigComponent,
|
||||||
ToggleButtonBasicConfigComponent,
|
ToggleButtonBasicConfigComponent,
|
||||||
TimeSeriesChartBasicConfigComponent
|
TimeSeriesChartBasicConfigComponent,
|
||||||
|
ComparisonKeyRowComponent,
|
||||||
|
ComparisonKeysTableComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<div [formGroup]="keyRowFormGroup" class="tb-form-table-row tb-comparison-key-row">
|
||||||
|
<mat-checkbox class="tb-show-field" formControlName="showValuesForComparison"></mat-checkbox>
|
||||||
|
<tb-data-key-input
|
||||||
|
[editable]="false"
|
||||||
|
[removable]="false"
|
||||||
|
[datasourceType]="datasourceType"
|
||||||
|
[formControl]="keyFormControl">
|
||||||
|
</tb-data-key-input>
|
||||||
|
<div class="tb-label-field">
|
||||||
|
<mat-form-field class="tb-inline-field" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="comparisonValuesLabel" placeholder="{{ 'widgets.time-series-chart.comparison.comparison-values-label-auto' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="tb-color-field">
|
||||||
|
<tb-color-input asBoxInput
|
||||||
|
colorClearButton
|
||||||
|
formControlName="color">
|
||||||
|
</tb-color-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
@import '../../../../../../../../scss/constants';
|
||||||
|
|
||||||
|
.tb-comparison-key-row {
|
||||||
|
.tb-show-field {
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-data-key-input {
|
||||||
|
flex: 1;
|
||||||
|
@media #{$mat-gt-xs} {
|
||||||
|
min-width: 100px;
|
||||||
|
flex: 1 1 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-label-field, .tb-color-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
place-content: center;
|
||||||
|
align-items: center;
|
||||||
|
.tb-inline-field {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-label-field {
|
||||||
|
flex: 1;
|
||||||
|
@media #{$mat-gt-xs} {
|
||||||
|
min-width: 150px;
|
||||||
|
flex: 1 1 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-color-field {
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,131 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { ChangeDetectorRef, Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
UntypedFormBuilder,
|
||||||
|
UntypedFormControl,
|
||||||
|
UntypedFormGroup
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
DataKey,
|
||||||
|
DataKeyComparisonSettings,
|
||||||
|
DataKeySettingsWithComparison,
|
||||||
|
DatasourceType
|
||||||
|
} from '@shared/models/widget.models';
|
||||||
|
import { deepClone } from '@core/utils';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-comparison-key-row',
|
||||||
|
templateUrl: './comparison-key-row.component.html',
|
||||||
|
styleUrls: ['./comparison-key-row.component.scss', '../../data-keys.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => ComparisonKeyRowComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ComparisonKeyRowComponent implements ControlValueAccessor, OnInit {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
datasourceType: DatasourceType;
|
||||||
|
|
||||||
|
keyFormControl: UntypedFormControl;
|
||||||
|
|
||||||
|
keyRowFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
|
modelValue: DataKey;
|
||||||
|
|
||||||
|
private propagateChange = (_val: any) => {};
|
||||||
|
|
||||||
|
constructor(private fb: UntypedFormBuilder,
|
||||||
|
private cd: ChangeDetectorRef) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.keyFormControl = this.fb.control(null, []);
|
||||||
|
this.keyRowFormGroup = this.fb.group({
|
||||||
|
showValuesForComparison: [null, []],
|
||||||
|
comparisonValuesLabel: [null, []],
|
||||||
|
color: [null, []]
|
||||||
|
});
|
||||||
|
this.keyRowFormGroup.valueChanges.subscribe(
|
||||||
|
() => this.updateModel()
|
||||||
|
);
|
||||||
|
this.keyRowFormGroup.get('showValuesForComparison').valueChanges.subscribe(() => this.updateValidators());
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
if (isDisabled) {
|
||||||
|
this.keyFormControl.disable({emitEvent: false});
|
||||||
|
this.keyRowFormGroup.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.keyFormControl.enable({emitEvent: false});
|
||||||
|
this.keyRowFormGroup.enable({emitEvent: false});
|
||||||
|
this.updateValidators();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: DataKey): void {
|
||||||
|
this.modelValue = value;
|
||||||
|
const comparisonSettings = (value?.settings as DataKeySettingsWithComparison)?.comparisonSettings;
|
||||||
|
this.keyRowFormGroup.patchValue(
|
||||||
|
comparisonSettings, {emitEvent: false}
|
||||||
|
);
|
||||||
|
this.keyFormControl.patchValue(deepClone(this.modelValue), {emitEvent: false});
|
||||||
|
this.updateValidators();
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateValidators() {
|
||||||
|
const showValuesForComparison: boolean = this.keyRowFormGroup.get('showValuesForComparison').value;
|
||||||
|
if (showValuesForComparison) {
|
||||||
|
this.keyFormControl.enable({emitEvent: false});
|
||||||
|
this.keyRowFormGroup.get('comparisonValuesLabel').enable({emitEvent: false});
|
||||||
|
this.keyRowFormGroup.get('color').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.keyFormControl.disable({emitEvent: false});
|
||||||
|
this.keyRowFormGroup.get('comparisonValuesLabel').disable({emitEvent: false});
|
||||||
|
this.keyRowFormGroup.get('color').disable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateModel() {
|
||||||
|
const comparisonSettings: DataKeyComparisonSettings = this.keyRowFormGroup.value;
|
||||||
|
if (!this.modelValue.settings) {
|
||||||
|
this.modelValue.settings = {};
|
||||||
|
}
|
||||||
|
this.modelValue.settings.comparisonSettings = comparisonSettings;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<div class="tb-comparison-keys-table tb-form-table">
|
||||||
|
<div class="tb-form-table-header">
|
||||||
|
<div class="tb-form-table-header-cell tb-show-header" translate>widgets.time-series-chart.comparison.show</div>
|
||||||
|
<div class="tb-form-table-header-cell tb-key-header" translate>datakey.key</div>
|
||||||
|
<div class="tb-form-table-header-cell tb-label-header" translate>datakey.label</div>
|
||||||
|
<div class="tb-form-table-header-cell tb-color-header" translate>datakey.color</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="keysFormArray().controls.length; else noKeys" class="tb-form-table-body">
|
||||||
|
<div *ngFor="let keyControl of keysFormArray().controls; trackBy: trackByKey; let $index = index;">
|
||||||
|
<tb-comparison-key-row
|
||||||
|
fxFlex
|
||||||
|
[datasourceType]="datasourceType"
|
||||||
|
[formControl]="keyControl">
|
||||||
|
</tb-comparison-key-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-template #noKeys>
|
||||||
|
<span fxLayoutAlign="center center"
|
||||||
|
class="tb-prompt">{{ 'widgets.chart.no-series' | translate }}</span>
|
||||||
|
</ng-template>
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
@import '../../../../../../../../scss/constants';
|
||||||
|
|
||||||
|
.tb-comparison-keys-table {
|
||||||
|
.tb-form-table-header-cell {
|
||||||
|
|
||||||
|
&.tb-show-header {
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tb-key-header {
|
||||||
|
flex: 1;
|
||||||
|
@media #{$mat-gt-xs} {
|
||||||
|
min-width: 100px;
|
||||||
|
flex: 1 1 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tb-label-header {
|
||||||
|
flex: 1;
|
||||||
|
@media #{$mat-gt-xs} {
|
||||||
|
min-width: 150px;
|
||||||
|
flex: 1 1 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tb-color-header {
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tb-form-table-body {
|
||||||
|
tb-comparison-key-row {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import {
|
||||||
|
AbstractControl,
|
||||||
|
ControlValueAccessor,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
UntypedFormArray,
|
||||||
|
UntypedFormBuilder,
|
||||||
|
UntypedFormGroup
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { DataKey, DatasourceType } from '@shared/models/widget.models';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-comparison-keys-table',
|
||||||
|
templateUrl: './comparison-keys-table.component.html',
|
||||||
|
styleUrls: ['./comparison-keys-table.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => ComparisonKeysTableComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ComparisonKeysTableComponent implements ControlValueAccessor, OnInit {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
datasourceType: DatasourceType;
|
||||||
|
|
||||||
|
keysListFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
|
get noKeys(): boolean {
|
||||||
|
const keys: DataKey[] = this.keysListFormGroup.get('keys').value;
|
||||||
|
return keys.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private propagateChange = (_val: any) => {};
|
||||||
|
|
||||||
|
constructor(private fb: UntypedFormBuilder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.keysListFormGroup = this.fb.group({
|
||||||
|
keys: [this.fb.array([]), []]
|
||||||
|
});
|
||||||
|
this.keysListFormGroup.valueChanges.subscribe(
|
||||||
|
() => {
|
||||||
|
const keys: DataKey[] = this.keysListFormGroup.get('keys').value;
|
||||||
|
this.propagateChange(keys);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.keysListFormGroup.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.keysListFormGroup.enable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: DataKey[] | undefined): void {
|
||||||
|
this.keysListFormGroup.setControl('keys', this.prepareKeysFormArray(value), {emitEvent: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
keysFormArray(): UntypedFormArray {
|
||||||
|
return this.keysListFormGroup.get('keys') as UntypedFormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackByKey(_index: number, keyControl: AbstractControl): any {
|
||||||
|
return keyControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepareKeysFormArray(keys: DataKey[] | undefined): UntypedFormArray {
|
||||||
|
const keysControls: Array<AbstractControl> = [];
|
||||||
|
if (keys) {
|
||||||
|
keys.forEach((key) => {
|
||||||
|
keysControls.push(this.fb.control(key, []));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.fb.array(keysControls);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -25,22 +25,77 @@
|
|||||||
forceSingleDatasource
|
forceSingleDatasource
|
||||||
formControlName="datasources">
|
formControlName="datasources">
|
||||||
</tb-datasources>
|
</tb-datasources>
|
||||||
<tb-data-keys-panel
|
<div class="tb-form-panel">
|
||||||
panelTitle="{{ 'widgets.chart.series' | translate }}"
|
<div fxLayout="row" fxLayoutAlign="space-between center">
|
||||||
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}"
|
<div class="tb-form-panel-title">{{ 'widgets.chart.series' | translate }}</div>
|
||||||
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}"
|
<tb-toggle-select [ngModel]="seriesMode" (ngModelChange)="seriesModeChange($event)"
|
||||||
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}"
|
[ngModelOptions]="{ standalone: true }">
|
||||||
noKeysText="{{ 'widgets.chart.no-series' | translate }}"
|
<tb-toggle-option value="series">{{ 'widgets.chart.series' | translate }}</tb-toggle-option>
|
||||||
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}"
|
<tb-toggle-option value="comparison">{{ 'widgets.time-series-chart.comparison.comparison' | translate }}</tb-toggle-option>
|
||||||
timeSeriesChart
|
</tb-toggle-select>
|
||||||
[yAxisIds]="yAxisIds"
|
</div>
|
||||||
[showTimeSeriesType]="chartType === TimeSeriesChartType.default"
|
<tb-data-keys-panel
|
||||||
hideSourceSelection
|
*ngIf="seriesMode === 'series'"
|
||||||
[datasourceType]="datasource?.type"
|
hidePanel
|
||||||
[deviceId]="datasource?.deviceId"
|
panelTitle="{{ 'widgets.chart.series' | translate }}"
|
||||||
[entityAliasId]="datasource?.entityAliasId"
|
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}"
|
||||||
formControlName="series">
|
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}"
|
||||||
</tb-data-keys-panel>
|
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}"
|
||||||
|
noKeysText="{{ 'widgets.chart.no-series' | translate }}"
|
||||||
|
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}"
|
||||||
|
timeSeriesChart
|
||||||
|
[yAxisIds]="yAxisIds"
|
||||||
|
[showTimeSeriesType]="chartType === TimeSeriesChartType.default"
|
||||||
|
hideSourceSelection
|
||||||
|
[datasourceType]="datasource?.type"
|
||||||
|
[deviceId]="datasource?.deviceId"
|
||||||
|
[entityAliasId]="datasource?.entityAliasId"
|
||||||
|
formControlName="series">
|
||||||
|
</tb-data-keys-panel>
|
||||||
|
<div *ngIf="seriesMode === 'comparison'" class="tb-form-row no-border no-padding column-xs">
|
||||||
|
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="comparisonEnabled">
|
||||||
|
{{ 'widgets.time-series-chart.comparison.comparison' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
|
||||||
|
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<mat-select formControlName="timeForComparison">
|
||||||
|
<mat-option [value]="'previousInterval'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-previous-interval' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'days'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-days' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'weeks'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-weeks' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'months'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-months' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'years'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-years' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'customInterval'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-custom-interval' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field [fxShow]="timeSeriesChartWidgetConfigForm.get('timeForComparison').value === 'customInterval'"
|
||||||
|
appearance="outline" class="number flex-lt-md" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="comparisonCustomIntervalValue" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
<tb-time-series-chart-axis-settings-button
|
||||||
|
axisType="xAxis"
|
||||||
|
panelTitle="{{ 'widgets.time-series-chart.axis.comparison-x-axis-settings' | translate }}"
|
||||||
|
formControlName="comparisonXAxis">
|
||||||
|
</tb-time-series-chart-axis-settings-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<tb-comparison-keys-table
|
||||||
|
*ngIf="seriesMode === 'comparison'"
|
||||||
|
[datasourceType]="datasource?.type"
|
||||||
|
formControlName="series">
|
||||||
|
</tb-comparison-keys-table>
|
||||||
|
</div>
|
||||||
<tb-time-series-chart-states-panel
|
<tb-time-series-chart-states-panel
|
||||||
*ngIf="chartType === TimeSeriesChartType.state"
|
*ngIf="chartType === TimeSeriesChartType.state"
|
||||||
formControlName="states">
|
formControlName="states">
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
|
|||||||
import {
|
import {
|
||||||
DataKey,
|
DataKey,
|
||||||
Datasource,
|
Datasource,
|
||||||
|
DatasourceType,
|
||||||
legendPositions,
|
legendPositions,
|
||||||
legendPositionTranslationMap,
|
legendPositionTranslationMap,
|
||||||
WidgetConfig,
|
WidgetConfig,
|
||||||
@ -89,6 +90,8 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
|
|
||||||
chartType: TimeSeriesChartType = TimeSeriesChartType.default;
|
chartType: TimeSeriesChartType = TimeSeriesChartType.default;
|
||||||
|
|
||||||
|
seriesMode = 'series';
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
protected widgetConfigComponent: WidgetConfigComponent,
|
protected widgetConfigComponent: WidgetConfigComponent,
|
||||||
private $injector: Injector,
|
private $injector: Injector,
|
||||||
@ -105,6 +108,11 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seriesModeChange(seriesMode: string) {
|
||||||
|
this.seriesMode = seriesMode;
|
||||||
|
this.updateSeriesState();
|
||||||
|
}
|
||||||
|
|
||||||
protected configForm(): UntypedFormGroup {
|
protected configForm(): UntypedFormGroup {
|
||||||
return this.timeSeriesChartWidgetConfigForm;
|
return this.timeSeriesChartWidgetConfigForm;
|
||||||
}
|
}
|
||||||
@ -135,6 +143,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
comparisonEnabled: [settings.comparisonEnabled, []],
|
comparisonEnabled: [settings.comparisonEnabled, []],
|
||||||
timeForComparison: [settings.timeForComparison, []],
|
timeForComparison: [settings.timeForComparison, []],
|
||||||
comparisonCustomIntervalValue: [settings.comparisonCustomIntervalValue, [Validators.min(0)]],
|
comparisonCustomIntervalValue: [settings.comparisonCustomIntervalValue, [Validators.min(0)]],
|
||||||
|
comparisonXAxis: [settings.comparisonXAxis, []],
|
||||||
|
|
||||||
thresholds: [settings.thresholds, []],
|
thresholds: [settings.thresholds, []],
|
||||||
|
|
||||||
@ -187,6 +196,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
if (this.chartType === TimeSeriesChartType.state) {
|
if (this.chartType === TimeSeriesChartType.state) {
|
||||||
this.timeSeriesChartWidgetConfigForm.addControl('states', this.fb.control(settings.states, []));
|
this.timeSeriesChartWidgetConfigForm.addControl('states', this.fb.control(settings.states, []));
|
||||||
}
|
}
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').valueChanges.subscribe(() => this.updateSeriesState());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
|
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
|
||||||
@ -209,6 +219,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
this.widgetConfig.config.settings.comparisonEnabled = config.comparisonEnabled;
|
this.widgetConfig.config.settings.comparisonEnabled = config.comparisonEnabled;
|
||||||
this.widgetConfig.config.settings.timeForComparison = config.timeForComparison;
|
this.widgetConfig.config.settings.timeForComparison = config.timeForComparison;
|
||||||
this.widgetConfig.config.settings.comparisonCustomIntervalValue = config.comparisonCustomIntervalValue;
|
this.widgetConfig.config.settings.comparisonCustomIntervalValue = config.comparisonCustomIntervalValue;
|
||||||
|
this.widgetConfig.config.settings.comparisonXAxis = config.comparisonXAxis;
|
||||||
|
|
||||||
this.widgetConfig.config.settings.thresholds = config.thresholds;
|
this.widgetConfig.config.settings.thresholds = config.thresholds;
|
||||||
|
|
||||||
@ -254,16 +265,27 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected validatorTriggers(): string[] {
|
protected validatorTriggers(): string[] {
|
||||||
return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate'];
|
return ['comparisonEnabled', 'showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate'];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateValidators(emitEvent: boolean, trigger?: string) {
|
protected updateValidators(emitEvent: boolean, trigger?: string) {
|
||||||
|
const comparisonEnabled: boolean = this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').value;
|
||||||
const showTitle: boolean = this.timeSeriesChartWidgetConfigForm.get('showTitle').value;
|
const showTitle: boolean = this.timeSeriesChartWidgetConfigForm.get('showTitle').value;
|
||||||
const showIcon: boolean = this.timeSeriesChartWidgetConfigForm.get('showIcon').value;
|
const showIcon: boolean = this.timeSeriesChartWidgetConfigForm.get('showIcon').value;
|
||||||
const showLegend: boolean = this.timeSeriesChartWidgetConfigForm.get('showLegend').value;
|
const showLegend: boolean = this.timeSeriesChartWidgetConfigForm.get('showLegend').value;
|
||||||
const showTooltip: boolean = this.timeSeriesChartWidgetConfigForm.get('showTooltip').value;
|
const showTooltip: boolean = this.timeSeriesChartWidgetConfigForm.get('showTooltip').value;
|
||||||
const tooltipShowDate: boolean = this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').value;
|
const tooltipShowDate: boolean = this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').value;
|
||||||
|
|
||||||
|
if (comparisonEnabled) {
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('timeForComparison').enable();
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('comparisonCustomIntervalValue').enable();
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('comparisonXAxis').enable();
|
||||||
|
} else {
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('timeForComparison').disable();
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('comparisonCustomIntervalValue').disable();
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('comparisonXAxis').disable();
|
||||||
|
}
|
||||||
|
|
||||||
if (showTitle) {
|
if (showTitle) {
|
||||||
this.timeSeriesChartWidgetConfigForm.get('title').enable();
|
this.timeSeriesChartWidgetConfigForm.get('title').enable();
|
||||||
this.timeSeriesChartWidgetConfigForm.get('titleFont').enable();
|
this.timeSeriesChartWidgetConfigForm.get('titleFont').enable();
|
||||||
@ -345,6 +367,19 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateSeriesState() {
|
||||||
|
if (this.seriesMode === 'series') {
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('series').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
const comparisonEnabled = this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').value;
|
||||||
|
if (comparisonEnabled) {
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('series').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.timeSeriesChartWidgetConfigForm.get('series').disable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private removeYaxisId(series: DataKey[], yAxisId: TimeSeriesChartYAxisId): boolean {
|
private removeYaxisId(series: DataKey[], yAxisId: TimeSeriesChartYAxisId): boolean {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
if (series) {
|
if (series) {
|
||||||
@ -381,4 +416,6 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
|
|||||||
processor.update(Date.now());
|
processor.update(Date.now());
|
||||||
return processor.formatted;
|
return processor.formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly DatasourceType = DatasourceType;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,10 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<div class="tb-form-panel tb-data-keys-panel">
|
<div class="tb-form-panel tb-data-keys-panel"
|
||||||
<div class="tb-form-panel-title">{{ panelTitle }}</div>
|
[class.no-border]="hidePanel"
|
||||||
|
[class.no-padding]="hidePanel">
|
||||||
|
<div *ngIf="!hidePanel" class="tb-form-panel-title">{{ panelTitle }}</div>
|
||||||
<div class="tb-form-table">
|
<div class="tb-form-table">
|
||||||
<div class="tb-form-table-header no-padding-right">
|
<div class="tb-form-table-header no-padding-right">
|
||||||
<div *ngIf="hasAdditionalLatestDataKeys" class="tb-form-table-header-cell tb-source-header" translate>datakey.source</div>
|
<div *ngIf="hasAdditionalLatestDataKeys" class="tb-form-table-header-cell tb-source-header" translate>datakey.source</div>
|
||||||
|
|||||||
@ -96,6 +96,10 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
|
|||||||
@Input()
|
@Input()
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
hidePanel = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
hideDataKeyColor = false;
|
hideDataKeyColor = false;
|
||||||
|
|||||||
@ -49,6 +49,7 @@ import { TbInject } from '@shared/decorators/tb-inject';
|
|||||||
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
|
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
|
||||||
import { UserSettingsService } from '@core/http/user-settings.service';
|
import { UserSettingsService } from '@core/http/user-settings.service';
|
||||||
import { ImagePipe } from '@shared/pipe/image.pipe';
|
import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||||
|
import { UtilsService } from '@core/services/utils.service';
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||||
@ -86,6 +87,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
|
|||||||
this.ctx.customDialog = $injector.get(CustomDialogService);
|
this.ctx.customDialog = $injector.get(CustomDialogService);
|
||||||
this.ctx.resourceService = $injector.get(ResourceService);
|
this.ctx.resourceService = $injector.get(ResourceService);
|
||||||
this.ctx.userSettingsService = $injector.get(UserSettingsService);
|
this.ctx.userSettingsService = $injector.get(UserSettingsService);
|
||||||
|
this.ctx.utilsService = $injector.get(UtilsService);
|
||||||
this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService);
|
this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService);
|
||||||
this.ctx.date = $injector.get(DatePipe);
|
this.ctx.date = $injector.get(DatePipe);
|
||||||
this.ctx.imagePipe = $injector.get(ImagePipe);
|
this.ctx.imagePipe = $injector.get(ImagePipe);
|
||||||
|
|||||||
@ -71,6 +71,7 @@ import { DatePipe } from '@angular/common';
|
|||||||
import { BuiltinTextPosition } from 'zrender/src/core/types';
|
import { BuiltinTextPosition } from 'zrender/src/core/types';
|
||||||
import { CartesianAxisOption } from 'echarts/types/src/coord/cartesian/AxisModel';
|
import { CartesianAxisOption } from 'echarts/types/src/coord/cartesian/AxisModel';
|
||||||
import { WidgetTimewindow } from '@shared/models/time/time.models';
|
import { WidgetTimewindow } from '@shared/models/time/time.models';
|
||||||
|
import { UtilsService } from '@core/services/utils.service';
|
||||||
|
|
||||||
export enum TimeSeriesChartType {
|
export enum TimeSeriesChartType {
|
||||||
default = 'default',
|
default = 'default',
|
||||||
@ -932,6 +933,7 @@ export interface TimeSeriesChartXAxis extends TimeSeriesChartAxis {
|
|||||||
export const createTimeSeriesYAxis = (units: string,
|
export const createTimeSeriesYAxis = (units: string,
|
||||||
decimals: number,
|
decimals: number,
|
||||||
settings: TimeSeriesChartYAxisSettings,
|
settings: TimeSeriesChartYAxisSettings,
|
||||||
|
utils: UtilsService,
|
||||||
darkMode: boolean): TimeSeriesChartYAxis => {
|
darkMode: boolean): TimeSeriesChartYAxis => {
|
||||||
const yAxisTickLabelStyle = createChartTextStyle(settings.tickLabelFont,
|
const yAxisTickLabelStyle = createChartTextStyle(settings.tickLabelFont,
|
||||||
settings.tickLabelColor, darkMode, 'axis.tickLabel');
|
settings.tickLabelColor, darkMode, 'axis.tickLabel');
|
||||||
@ -986,7 +988,7 @@ export const createTimeSeriesYAxis = (units: string,
|
|||||||
splitNumber,
|
splitNumber,
|
||||||
interval,
|
interval,
|
||||||
ticksGenerator,
|
ticksGenerator,
|
||||||
name: settings.label,
|
name: utils.customTranslation(settings.label, settings.label),
|
||||||
nameLocation: 'middle',
|
nameLocation: 'middle',
|
||||||
nameRotate: settings.position === AxisPosition.left ? 90 : -90,
|
nameRotate: settings.position === AxisPosition.left ? 90 : -90,
|
||||||
nameTextStyle: {
|
nameTextStyle: {
|
||||||
@ -1044,6 +1046,7 @@ export const createTimeSeriesXAxis = (id: string,
|
|||||||
settings: TimeSeriesChartXAxisSettings,
|
settings: TimeSeriesChartXAxisSettings,
|
||||||
min: number, max: number,
|
min: number, max: number,
|
||||||
datePipe: DatePipe,
|
datePipe: DatePipe,
|
||||||
|
utils: UtilsService,
|
||||||
darkMode: boolean): TimeSeriesChartXAxis => {
|
darkMode: boolean): TimeSeriesChartXAxis => {
|
||||||
const xAxisTickLabelStyle = createChartTextStyle(settings.tickLabelFont,
|
const xAxisTickLabelStyle = createChartTextStyle(settings.tickLabelFont,
|
||||||
settings.tickLabelColor, darkMode, 'axis.tickLabel');
|
settings.tickLabelColor, darkMode, 'axis.tickLabel');
|
||||||
@ -1060,7 +1063,7 @@ export const createTimeSeriesXAxis = (id: string,
|
|||||||
scale: true,
|
scale: true,
|
||||||
position: settings.position,
|
position: settings.position,
|
||||||
id,
|
id,
|
||||||
name: settings.label,
|
name: utils.customTranslation(settings.label, settings.label),
|
||||||
nameLocation: 'middle',
|
nameLocation: 'middle',
|
||||||
nameTextStyle: {
|
nameTextStyle: {
|
||||||
color: xAxisNameStyle.color,
|
color: xAxisNameStyle.color,
|
||||||
|
|||||||
@ -170,7 +170,7 @@ export class TbTimeSeriesChart {
|
|||||||
this.comparisonEnabled = !!this.ctx.defaultSubscription.comparisonEnabled;
|
this.comparisonEnabled = !!this.ctx.defaultSubscription.comparisonEnabled;
|
||||||
this.stackMode = !this.comparisonEnabled && this.settings.stack;
|
this.stackMode = !this.comparisonEnabled && this.settings.stack;
|
||||||
if (this.settings.states && this.settings.states.length) {
|
if (this.settings.states && this.settings.states.length) {
|
||||||
this.stateValueConverter = new TimeSeriesChartStateValueConverter(this.ctx.dashboard.utils, this.settings.states);
|
this.stateValueConverter = new TimeSeriesChartStateValueConverter(this.ctx.utilsService, this.settings.states);
|
||||||
this.tooltipValueFormatFunction = this.stateValueConverter.tooltipFormatter;
|
this.tooltipValueFormatFunction = this.stateValueConverter.tooltipFormatter;
|
||||||
}
|
}
|
||||||
const $dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page');
|
const $dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page');
|
||||||
@ -503,12 +503,12 @@ export class TbTimeSeriesChart {
|
|||||||
|
|
||||||
private setupXAxes(): void {
|
private setupXAxes(): void {
|
||||||
const mainXAxis = createTimeSeriesXAxis('main', this.settings.xAxis, this.ctx.defaultSubscription.timeWindow.minTime,
|
const mainXAxis = createTimeSeriesXAxis('main', this.settings.xAxis, this.ctx.defaultSubscription.timeWindow.minTime,
|
||||||
this.ctx.defaultSubscription.timeWindow.maxTime, this.ctx.date, this.darkMode);
|
this.ctx.defaultSubscription.timeWindow.maxTime, this.ctx.date, this.ctx.utilsService, this.darkMode);
|
||||||
this.xAxisList.push(mainXAxis);
|
this.xAxisList.push(mainXAxis);
|
||||||
if (this.comparisonEnabled) {
|
if (this.comparisonEnabled) {
|
||||||
const comparisonXAxis = createTimeSeriesXAxis('comparison', this.settings.comparisonXAxis,
|
const comparisonXAxis = createTimeSeriesXAxis('comparison', this.settings.comparisonXAxis,
|
||||||
this.ctx.defaultSubscription.comparisonTimeWindow.minTime, this.ctx.defaultSubscription.comparisonTimeWindow.maxTime,
|
this.ctx.defaultSubscription.comparisonTimeWindow.minTime, this.ctx.defaultSubscription.comparisonTimeWindow.maxTime,
|
||||||
this.ctx.date, this.darkMode);
|
this.ctx.date, this.ctx.utilsService, this.darkMode);
|
||||||
this.xAxisList.push(comparisonXAxis);
|
this.xAxisList.push(comparisonXAxis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,7 +526,7 @@ export class TbTimeSeriesChart {
|
|||||||
axisSettings.ticksGenerator = this.stateValueConverter.ticksGenerator;
|
axisSettings.ticksGenerator = this.stateValueConverter.ticksGenerator;
|
||||||
axisSettings.ticksFormatter = this.stateValueConverter.ticksFormatter;
|
axisSettings.ticksFormatter = this.stateValueConverter.ticksFormatter;
|
||||||
}
|
}
|
||||||
const yAxis = createTimeSeriesYAxis(units, decimals, axisSettings, this.darkMode);
|
const yAxis = createTimeSeriesYAxis(units, decimals, axisSettings, this.ctx.utilsService, this.darkMode);
|
||||||
this.yAxisList.push(yAxis);
|
this.yAxisList.push(yAxis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,35 @@
|
|||||||
helpId="widget/lib/flot/tooltip_value_format_fn">
|
helpId="widget/lib/flot/tooltip_value_format_fn">
|
||||||
</tb-js-func>
|
</tb-js-func>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="comparisonEnabled" class="tb-form-panel tb-slide-toggle" formGroupName="comparisonSettings">
|
||||||
|
<div class="tb-form-panel-title" translate>widgets.time-series-chart.comparison.settings</div>
|
||||||
|
<mat-expansion-panel class="tb-settings" [expanded]="timeSeriesChartKeySettingsForm.get('comparisonSettings.showValuesForComparison').value"
|
||||||
|
[disabled]="!timeSeriesChartKeySettingsForm.get('comparisonSettings.showValuesForComparison').value">
|
||||||
|
<mat-expansion-panel-header fxLayout="row wrap">
|
||||||
|
<mat-panel-title>
|
||||||
|
<mat-slide-toggle class="mat-slide" formControlName="showValuesForComparison" (click)="$event.stopPropagation()"
|
||||||
|
fxLayoutAlign="center">
|
||||||
|
{{ 'widgets.time-series-chart.comparison.show-values-for-comparison' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-template matExpansionPanelContent>
|
||||||
|
<div class="tb-form-row">
|
||||||
|
<div class="fixed-title-width" translate>widgets.time-series-chart.comparison.comparison-values-label</div>
|
||||||
|
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="comparisonValuesLabel" placeholder="{{ 'widgets.time-series-chart.comparison.comparison-values-label-auto' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="tb-form-row space-between">
|
||||||
|
<div>{{ 'widgets.time-series-chart.comparison.comparison-data-color' | translate }}</div>
|
||||||
|
<tb-color-input asBoxInput
|
||||||
|
colorClearButton
|
||||||
|
formControlName="color">
|
||||||
|
</tb-color-input>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #chartTypeTitle>
|
<ng-template #chartTypeTitle>
|
||||||
<div class="tb-form-panel-title">{{ timeSeriesChartTypeTranslations.get(chartType) | translate }}</div>
|
<div class="tb-form-panel-title">{{ timeSeriesChartTypeTranslations.get(chartType) | translate }}</div>
|
||||||
|
|||||||
@ -54,6 +54,8 @@ export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent
|
|||||||
|
|
||||||
yAxisIds: TimeSeriesChartYAxisId[];
|
yAxisIds: TimeSeriesChartYAxisId[];
|
||||||
|
|
||||||
|
comparisonEnabled: boolean;
|
||||||
|
|
||||||
functionScopeVariables = this.widgetService.getWidgetScopeVariables();
|
functionScopeVariables = this.widgetService.getWidgetScopeVariables();
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
@ -73,6 +75,7 @@ export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent
|
|||||||
}
|
}
|
||||||
const widgetSettings = (widgetConfig.config?.settings || {}) as TimeSeriesChartWidgetSettings;
|
const widgetSettings = (widgetConfig.config?.settings || {}) as TimeSeriesChartWidgetSettings;
|
||||||
this.yAxisIds = widgetSettings.yAxes ? Object.keys(widgetSettings.yAxes) : ['default'];
|
this.yAxisIds = widgetSettings.yAxes ? Object.keys(widgetSettings.yAxes) : ['default'];
|
||||||
|
this.comparisonEnabled = !!widgetSettings.comparisonEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected defaultSettings(): WidgetSettings {
|
protected defaultSettings(): WidgetSettings {
|
||||||
@ -103,12 +106,14 @@ export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected validatorTriggers(): string[] {
|
protected validatorTriggers(): string[] {
|
||||||
return ['showInLegend', 'type'];
|
return ['showInLegend', 'type', 'comparisonSettings.showValuesForComparison'];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateValidators(_emitEvent: boolean) {
|
protected updateValidators(_emitEvent: boolean) {
|
||||||
const showInLegend: boolean = this.timeSeriesChartKeySettingsForm.get('showInLegend').value;
|
const showInLegend: boolean = this.timeSeriesChartKeySettingsForm.get('showInLegend').value;
|
||||||
const type: TimeSeriesChartSeriesType = this.timeSeriesChartKeySettingsForm.get('type').value;
|
const type: TimeSeriesChartSeriesType = this.timeSeriesChartKeySettingsForm.get('type').value;
|
||||||
|
const showValuesForComparison: boolean =
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').get('showValuesForComparison').value;
|
||||||
if (showInLegend) {
|
if (showInLegend) {
|
||||||
this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').enable();
|
this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').enable();
|
||||||
} else {
|
} else {
|
||||||
@ -122,5 +127,17 @@ export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent
|
|||||||
this.timeSeriesChartKeySettingsForm.get('lineSettings').disable();
|
this.timeSeriesChartKeySettingsForm.get('lineSettings').disable();
|
||||||
this.timeSeriesChartKeySettingsForm.get('barSettings').enable();
|
this.timeSeriesChartKeySettingsForm.get('barSettings').enable();
|
||||||
}
|
}
|
||||||
|
if (this.comparisonEnabled) {
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').enable({emitEvent: false});
|
||||||
|
if (showValuesForComparison) {
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').get('comparisonValuesLabel').enable();
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').get('color').enable();
|
||||||
|
} else {
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').get('comparisonValuesLabel').disable();
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').get('color').disable();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.timeSeriesChartKeySettingsForm.get('comparisonSettings').disable({emitEvent: false});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,46 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
<ng-container [formGroup]="timeSeriesChartWidgetSettingsForm">
|
<ng-container [formGroup]="timeSeriesChartWidgetSettingsForm">
|
||||||
|
<div class="tb-form-panel">
|
||||||
|
<div class="tb-form-row no-border no-padding column-xs">
|
||||||
|
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="comparisonEnabled">
|
||||||
|
{{ 'widgets.time-series-chart.comparison.comparison' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
|
||||||
|
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<mat-select formControlName="timeForComparison">
|
||||||
|
<mat-option [value]="'previousInterval'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-previous-interval' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'days'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-days' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'weeks'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-weeks' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'months'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-months' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'years'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-years' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
<mat-option [value]="'customInterval'">
|
||||||
|
{{ 'widgets.chart.time-for-comparison-custom-interval' | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field [fxShow]="timeSeriesChartWidgetSettingsForm.get('timeForComparison').value === 'customInterval'"
|
||||||
|
appearance="outline" class="number flex-lt-md" subscriptSizing="dynamic">
|
||||||
|
<input matInput formControlName="comparisonCustomIntervalValue" type="number" min="0" placeholder="{{ 'widget-config.set' | translate }}">
|
||||||
|
</mat-form-field>
|
||||||
|
<tb-time-series-chart-axis-settings-button
|
||||||
|
axisType="xAxis"
|
||||||
|
panelTitle="{{ 'widgets.time-series-chart.axis.comparison-x-axis-settings' | translate }}"
|
||||||
|
formControlName="comparisonXAxis">
|
||||||
|
</tb-time-series-chart-axis-settings-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<tb-time-series-chart-states-panel
|
<tb-time-series-chart-states-panel
|
||||||
*ngIf="chartType === TimeSeriesChartType.state"
|
*ngIf="chartType === TimeSeriesChartType.state"
|
||||||
formControlName="states">
|
formControlName="states">
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import {
|
|||||||
WidgetSettings,
|
WidgetSettings,
|
||||||
WidgetSettingsComponent
|
WidgetSettingsComponent
|
||||||
} from '@shared/models/widget.models';
|
} from '@shared/models/widget.models';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { formatValue, isDefinedAndNotNull, mergeDeep } from '@core/utils';
|
import { formatValue, isDefinedAndNotNull, mergeDeep } from '@core/utils';
|
||||||
@ -114,6 +114,11 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon
|
|||||||
protected onSettingsSet(settings: WidgetSettings) {
|
protected onSettingsSet(settings: WidgetSettings) {
|
||||||
this.timeSeriesChartWidgetSettingsForm = this.fb.group({
|
this.timeSeriesChartWidgetSettingsForm = this.fb.group({
|
||||||
|
|
||||||
|
comparisonEnabled: [settings.comparisonEnabled, []],
|
||||||
|
timeForComparison: [settings.timeForComparison, []],
|
||||||
|
comparisonCustomIntervalValue: [settings.comparisonCustomIntervalValue, [Validators.min(0)]],
|
||||||
|
comparisonXAxis: [settings.comparisonXAxis, []],
|
||||||
|
|
||||||
yAxes: [settings.yAxes, []],
|
yAxes: [settings.yAxes, []],
|
||||||
thresholds: [settings.thresholds, []],
|
thresholds: [settings.thresholds, []],
|
||||||
|
|
||||||
@ -154,14 +159,25 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected validatorTriggers(): string[] {
|
protected validatorTriggers(): string[] {
|
||||||
return ['showLegend', 'showTooltip', 'tooltipShowDate'];
|
return ['comparisonEnabled', 'showLegend', 'showTooltip', 'tooltipShowDate'];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateValidators(emitEvent: boolean) {
|
protected updateValidators(emitEvent: boolean) {
|
||||||
|
const comparisonEnabled: boolean = this.timeSeriesChartWidgetSettingsForm.get('comparisonEnabled').value;
|
||||||
const showLegend: boolean = this.timeSeriesChartWidgetSettingsForm.get('showLegend').value;
|
const showLegend: boolean = this.timeSeriesChartWidgetSettingsForm.get('showLegend').value;
|
||||||
const showTooltip: boolean = this.timeSeriesChartWidgetSettingsForm.get('showTooltip').value;
|
const showTooltip: boolean = this.timeSeriesChartWidgetSettingsForm.get('showTooltip').value;
|
||||||
const tooltipShowDate: boolean = this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').value;
|
const tooltipShowDate: boolean = this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').value;
|
||||||
|
|
||||||
|
if (comparisonEnabled) {
|
||||||
|
this.timeSeriesChartWidgetSettingsForm.get('timeForComparison').enable();
|
||||||
|
this.timeSeriesChartWidgetSettingsForm.get('comparisonCustomIntervalValue').enable();
|
||||||
|
this.timeSeriesChartWidgetSettingsForm.get('comparisonXAxis').enable();
|
||||||
|
} else {
|
||||||
|
this.timeSeriesChartWidgetSettingsForm.get('timeForComparison').disable();
|
||||||
|
this.timeSeriesChartWidgetSettingsForm.get('comparisonCustomIntervalValue').disable();
|
||||||
|
this.timeSeriesChartWidgetSettingsForm.get('comparisonXAxis').disable();
|
||||||
|
}
|
||||||
|
|
||||||
if (showLegend) {
|
if (showLegend) {
|
||||||
this.timeSeriesChartWidgetSettingsForm.get('legendLabelFont').enable();
|
this.timeSeriesChartWidgetSettingsForm.get('legendLabelFont').enable();
|
||||||
this.timeSeriesChartWidgetSettingsForm.get('legendLabelColor').enable();
|
this.timeSeriesChartWidgetSettingsForm.get('legendLabelColor').enable();
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<button type="button"
|
||||||
|
mat-stroked-button
|
||||||
|
color="primary"
|
||||||
|
[disabled]="disabled"
|
||||||
|
#matButton
|
||||||
|
(click)="openAxisSettingsPopup($event, matButton)"
|
||||||
|
matTooltip="{{ panelTitle }}"
|
||||||
|
matTooltipPosition="above">
|
||||||
|
{{ (axisType === 'xAxis' ? 'widgets.time-series-chart.axis.x-axis' : 'widgets.time-series-chart.axis.y-axis') | translate }}
|
||||||
|
<tb-icon matButtonIcon>settings</tb-icon>
|
||||||
|
</button>
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
|
||||||
|
import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { MatButton } from '@angular/material/button';
|
||||||
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
import { TimeSeriesChartAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models';
|
||||||
|
import {
|
||||||
|
TimeSeriesChartAxisSettingsPanelComponent
|
||||||
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-time-series-chart-axis-settings-button',
|
||||||
|
templateUrl: './time-series-chart-axis-settings-button.component.html',
|
||||||
|
styleUrls: [],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => TimeSeriesChartAxisSettingsButtonComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TimeSeriesChartAxisSettingsButtonComponent implements OnInit, ControlValueAccessor {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
axisType: 'xAxis' | 'yAxis' = 'xAxis';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
panelTitle: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
advanced = false;
|
||||||
|
|
||||||
|
private modelValue: TimeSeriesChartAxisSettings;
|
||||||
|
|
||||||
|
private propagateChange = null;
|
||||||
|
|
||||||
|
constructor(private popoverService: TbPopoverService,
|
||||||
|
private renderer: Renderer2,
|
||||||
|
private viewContainerRef: ViewContainerRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: TimeSeriesChartAxisSettings): void {
|
||||||
|
this.modelValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
openAxisSettingsPopup($event: Event, matButton: MatButton) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
const trigger = matButton._elementRef.nativeElement;
|
||||||
|
if (this.popoverService.hasPopover(trigger)) {
|
||||||
|
this.popoverService.hidePopover(trigger);
|
||||||
|
} else {
|
||||||
|
const ctx: any = {
|
||||||
|
axisSettings: this.modelValue,
|
||||||
|
axisType: this.axisType,
|
||||||
|
panelTitle: this.panelTitle,
|
||||||
|
advanced: this.advanced
|
||||||
|
};
|
||||||
|
const axisSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
||||||
|
this.viewContainerRef, TimeSeriesChartAxisSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null,
|
||||||
|
ctx,
|
||||||
|
{},
|
||||||
|
{}, {}, true);
|
||||||
|
axisSettingsPanelPopover.tbComponentRef.instance.popover = axisSettingsPanelPopover;
|
||||||
|
axisSettingsPanelPopover.tbComponentRef.instance.axisSettingsApplied.subscribe((axisSettings) => {
|
||||||
|
axisSettingsPanelPopover.hide();
|
||||||
|
this.modelValue = axisSettings;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -15,17 +15,17 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<div class="tb-y-axis-settings-panel" [formGroup]="yAxisSettingsFormGroup">
|
<div class="tb-axis-settings-panel" [formGroup]="axisSettingsFormGroup">
|
||||||
<div class="tb-y-axis-settings-title">{{ 'widgets.time-series-chart.axis.y-axis-settings' | translate }}</div>
|
<div class="tb-axis-settings-title">{{ panelTitle }}</div>
|
||||||
<div class="tb-y-axis-settings-panel-content">
|
<div class="tb-axis-settings-panel-content">
|
||||||
<tb-time-series-chart-axis-settings
|
<tb-time-series-chart-axis-settings
|
||||||
formControlName="yAxis"
|
formControlName="axis"
|
||||||
alwaysExpanded
|
alwaysExpanded
|
||||||
[advanced]="advanced"
|
[advanced]="advanced"
|
||||||
axisType="yAxis">
|
[axisType]="axisType">
|
||||||
</tb-time-series-chart-axis-settings>
|
</tb-time-series-chart-axis-settings>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-y-axis-settings-panel-buttons">
|
<div class="tb-axis-settings-panel-buttons">
|
||||||
<button mat-button
|
<button mat-button
|
||||||
color="primary"
|
color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
@ -35,8 +35,8 @@
|
|||||||
<button mat-raised-button
|
<button mat-raised-button
|
||||||
color="primary"
|
color="primary"
|
||||||
type="button"
|
type="button"
|
||||||
(click)="applyYAxisSettings()"
|
(click)="applyAxisSettings()"
|
||||||
[disabled]="yAxisSettingsFormGroup.invalid || !yAxisSettingsFormGroup.dirty">
|
[disabled]="axisSettingsFormGroup.invalid || !axisSettingsFormGroup.dirty">
|
||||||
{{ 'action.apply' | translate }}
|
{{ 'action.apply' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
@import '../../../../../../../../../scss/constants';
|
@import '../../../../../../../../../scss/constants';
|
||||||
|
|
||||||
.tb-y-axis-settings-panel {
|
.tb-axis-settings-panel {
|
||||||
width: 530px;
|
width: 530px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -23,7 +23,7 @@
|
|||||||
@media #{$mat-lt-md} {
|
@media #{$mat-lt-md} {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
}
|
}
|
||||||
.tb-y-axis-settings-panel-content {
|
.tb-axis-settings-panel-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
@ -31,14 +31,14 @@
|
|||||||
margin: -10px;
|
margin: -10px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
.tb-y-axis-settings-title {
|
.tb-axis-settings-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
letter-spacing: 0.25px;
|
letter-spacing: 0.25px;
|
||||||
color: rgba(0, 0, 0, 0.87);
|
color: rgba(0, 0, 0, 0.87);
|
||||||
}
|
}
|
||||||
.tb-y-axis-settings-panel-buttons {
|
.tb-axis-settings-panel-buttons {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -17,40 +17,49 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
import { TimeSeriesChartYAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models';
|
import {
|
||||||
|
TimeSeriesChartAxisSettings,
|
||||||
|
TimeSeriesChartYAxisSettings
|
||||||
|
} from '@home/components/widget/lib/chart/time-series-chart.models';
|
||||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-time-series-chart-y-axis-settings-panel',
|
selector: 'tb-time-series-chart-axis-settings-panel',
|
||||||
templateUrl: './time-series-chart-y-axis-settings-panel.component.html',
|
templateUrl: './time-series-chart-axis-settings-panel.component.html',
|
||||||
providers: [],
|
providers: [],
|
||||||
styleUrls: ['./time-series-chart-y-axis-settings-panel.component.scss'],
|
styleUrls: ['./time-series-chart-axis-settings-panel.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class TimeSeriesChartYAxisSettingsPanelComponent implements OnInit {
|
export class TimeSeriesChartAxisSettingsPanelComponent implements OnInit {
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
yAxisSettings: TimeSeriesChartYAxisSettings;
|
axisType: 'xAxis' | 'yAxis' = 'xAxis';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
panelTitle: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
axisSettings: TimeSeriesChartAxisSettings;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
advanced = false;
|
advanced = false;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
popover: TbPopoverComponent<TimeSeriesChartYAxisSettingsPanelComponent>;
|
popover: TbPopoverComponent<TimeSeriesChartAxisSettingsPanelComponent>;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
yAxisSettingsApplied = new EventEmitter<TimeSeriesChartYAxisSettings>();
|
axisSettingsApplied = new EventEmitter<TimeSeriesChartAxisSettings>();
|
||||||
|
|
||||||
yAxisSettingsFormGroup: UntypedFormGroup;
|
axisSettingsFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder) {
|
constructor(private fb: UntypedFormBuilder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.yAxisSettingsFormGroup = this.fb.group(
|
this.axisSettingsFormGroup = this.fb.group(
|
||||||
{
|
{
|
||||||
yAxis: [this.yAxisSettings, []]
|
axis: [this.axisSettings, []]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -59,8 +68,8 @@ export class TimeSeriesChartYAxisSettingsPanelComponent implements OnInit {
|
|||||||
this.popover?.hide();
|
this.popover?.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
applyYAxisSettings() {
|
applyAxisSettings() {
|
||||||
const yAxisSettings = this.yAxisSettingsFormGroup.get('yAxis').getRawValue();
|
const axisSettings = this.axisSettingsFormGroup.get('axis').getRawValue();
|
||||||
this.yAxisSettingsApplied.emit(yAxisSettings);
|
this.axisSettingsApplied.emit(axisSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,9 +36,10 @@ import { MatButton } from '@angular/material/button';
|
|||||||
import { TbPopoverService } from '@shared/components/popover.service';
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
import {
|
import {
|
||||||
TimeSeriesChartYAxisSettingsPanelComponent
|
TimeSeriesChartAxisSettingsPanelComponent
|
||||||
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component';
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component';
|
||||||
import { deepClone } from '@core/utils';
|
import { deepClone } from '@core/utils';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-time-series-chart-y-axis-row',
|
selector: 'tb-time-series-chart-y-axis-row',
|
||||||
@ -76,6 +77,7 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O
|
|||||||
private propagateChange = (_val: any) => {};
|
private propagateChange = (_val: any) => {};
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder,
|
constructor(private fb: UntypedFormBuilder,
|
||||||
|
private translate: TranslateService,
|
||||||
private popoverService: TbPopoverService,
|
private popoverService: TbPopoverService,
|
||||||
private renderer: Renderer2,
|
private renderer: Renderer2,
|
||||||
private viewContainerRef: ViewContainerRef,
|
private viewContainerRef: ViewContainerRef,
|
||||||
@ -143,16 +145,18 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O
|
|||||||
this.popoverService.hidePopover(trigger);
|
this.popoverService.hidePopover(trigger);
|
||||||
} else {
|
} else {
|
||||||
const ctx: any = {
|
const ctx: any = {
|
||||||
yAxisSettings: deepClone(this.modelValue),
|
axisType: 'yAxis',
|
||||||
|
panelTitle: this.translate.instant('widgets.time-series-chart.axis.y-axis-settings'),
|
||||||
|
axisSettings: deepClone(this.modelValue),
|
||||||
advanced: this.advanced
|
advanced: this.advanced
|
||||||
};
|
};
|
||||||
const yAxisSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
const yAxisSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
||||||
this.viewContainerRef, TimeSeriesChartYAxisSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null,
|
this.viewContainerRef, TimeSeriesChartAxisSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null,
|
||||||
ctx,
|
ctx,
|
||||||
{},
|
{},
|
||||||
{}, {}, true);
|
{}, {}, true);
|
||||||
yAxisSettingsPanelPopover.tbComponentRef.instance.popover = yAxisSettingsPanelPopover;
|
yAxisSettingsPanelPopover.tbComponentRef.instance.popover = yAxisSettingsPanelPopover;
|
||||||
yAxisSettingsPanelPopover.tbComponentRef.instance.yAxisSettingsApplied.subscribe((yAxisSettings) => {
|
yAxisSettingsPanelPopover.tbComponentRef.instance.axisSettingsApplied.subscribe((yAxisSettings) => {
|
||||||
yAxisSettingsPanelPopover.hide();
|
yAxisSettingsPanelPopover.hide();
|
||||||
this.modelValue = {...this.modelValue, ...yAxisSettings};
|
this.modelValue = {...this.modelValue, ...yAxisSettings};
|
||||||
this.axisFormGroup.patchValue(
|
this.axisFormGroup.patchValue(
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
[class.tb-suffix-absolute]="!keysFormControl.value?.length">
|
[class.tb-suffix-absolute]="!keysFormControl.value?.length">
|
||||||
<mat-chip-grid #chipList [formControl]="keysFormControl">
|
<mat-chip-grid #chipList [formControl]="keysFormControl">
|
||||||
<mat-chip-row class="tb-datakey-chip" *ngIf="modelValue?.type"
|
<mat-chip-row class="tb-datakey-chip" *ngIf="modelValue?.type"
|
||||||
|
[removable]="removable"
|
||||||
(removed)="removeKey()">
|
(removed)="removeKey()">
|
||||||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="4px" class="tb-attribute-chip">
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="4px" class="tb-attribute-chip">
|
||||||
<div class="tb-chip-labels">
|
<div class="tb-chip-labels">
|
||||||
@ -49,7 +50,8 @@
|
|||||||
(click)="editKey()" mat-icon-button class="tb-mat-24">
|
(click)="editKey()" mat-icon-button class="tb-mat-24">
|
||||||
<mat-icon class="tb-mat-18">edit</mat-icon>
|
<mat-icon class="tb-mat-18">edit</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button matChipRemove
|
<button *ngIf="removable"
|
||||||
|
matChipRemove
|
||||||
type="button"
|
type="button"
|
||||||
mat-icon-button class="tb-mat-24">
|
mat-icon-button class="tb-mat-24">
|
||||||
<mat-icon class="tb-mat-18">close</mat-icon>
|
<mat-icon class="tb-mat-18">close</mat-icon>
|
||||||
|
|||||||
@ -16,6 +16,13 @@
|
|||||||
.tb-data-key-input {
|
.tb-data-key-input {
|
||||||
.mat-mdc-form-field.tb-inline-field.tb-key-field {
|
.mat-mdc-form-field.tb-inline-field.tb-key-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
&.mat-form-field-appearance-fill {
|
||||||
|
.mdc-text-field--filled.mdc-text-field--disabled {
|
||||||
|
&:before {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) {
|
.mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
|||||||
@ -98,6 +98,10 @@ export class DataKeyInputComponent implements ControlValueAccessor, OnInit, OnCh
|
|||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
editable = true;
|
editable = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
removable = true;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
datasourceType: DatasourceType;
|
datasourceType: DatasourceType;
|
||||||
|
|
||||||
|
|||||||
@ -116,8 +116,8 @@ import {
|
|||||||
TimeSeriesChartYAxisRowComponent
|
TimeSeriesChartYAxisRowComponent
|
||||||
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component';
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component';
|
||||||
import {
|
import {
|
||||||
TimeSeriesChartYAxisSettingsPanelComponent
|
TimeSeriesChartAxisSettingsPanelComponent
|
||||||
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component';
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-panel.component';
|
||||||
import {
|
import {
|
||||||
TimeSeriesChartAnimationSettingsComponent
|
TimeSeriesChartAnimationSettingsComponent
|
||||||
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component';
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component';
|
||||||
@ -139,6 +139,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
TimeSeriesChartStatesPanelComponent
|
TimeSeriesChartStatesPanelComponent
|
||||||
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-states-panel.component';
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-states-panel.component';
|
||||||
|
import {
|
||||||
|
TimeSeriesChartAxisSettingsButtonComponent
|
||||||
|
} from '@home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings-button.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -184,7 +187,8 @@ import {
|
|||||||
TimeSeriesNoAggregationBarWidthSettingsComponent,
|
TimeSeriesNoAggregationBarWidthSettingsComponent,
|
||||||
TimeSeriesChartYAxesPanelComponent,
|
TimeSeriesChartYAxesPanelComponent,
|
||||||
TimeSeriesChartYAxisRowComponent,
|
TimeSeriesChartYAxisRowComponent,
|
||||||
TimeSeriesChartYAxisSettingsPanelComponent,
|
TimeSeriesChartAxisSettingsPanelComponent,
|
||||||
|
TimeSeriesChartAxisSettingsButtonComponent,
|
||||||
TimeSeriesChartAnimationSettingsComponent,
|
TimeSeriesChartAnimationSettingsComponent,
|
||||||
TimeSeriesChartFillSettingsComponent,
|
TimeSeriesChartFillSettingsComponent,
|
||||||
TimeSeriesChartThresholdSettingsComponent,
|
TimeSeriesChartThresholdSettingsComponent,
|
||||||
@ -241,7 +245,8 @@ import {
|
|||||||
TimeSeriesNoAggregationBarWidthSettingsComponent,
|
TimeSeriesNoAggregationBarWidthSettingsComponent,
|
||||||
TimeSeriesChartYAxesPanelComponent,
|
TimeSeriesChartYAxesPanelComponent,
|
||||||
TimeSeriesChartYAxisRowComponent,
|
TimeSeriesChartYAxisRowComponent,
|
||||||
TimeSeriesChartYAxisSettingsPanelComponent,
|
TimeSeriesChartAxisSettingsPanelComponent,
|
||||||
|
TimeSeriesChartAxisSettingsButtonComponent,
|
||||||
TimeSeriesChartAnimationSettingsComponent,
|
TimeSeriesChartAnimationSettingsComponent,
|
||||||
TimeSeriesChartFillSettingsComponent,
|
TimeSeriesChartFillSettingsComponent,
|
||||||
TimeSeriesChartThresholdSettingsComponent,
|
TimeSeriesChartThresholdSettingsComponent,
|
||||||
|
|||||||
@ -103,6 +103,7 @@ import { UserId } from '@shared/models/id/user-id';
|
|||||||
import { UserSettingsService } from '@core/http/user-settings.service';
|
import { UserSettingsService } from '@core/http/user-settings.service';
|
||||||
import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service';
|
import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service';
|
||||||
import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
|
import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
|
||||||
|
import { UtilsService } from '@core/services/utils.service';
|
||||||
|
|
||||||
export interface IWidgetAction {
|
export interface IWidgetAction {
|
||||||
name: string;
|
name: string;
|
||||||
@ -194,6 +195,7 @@ export class WidgetContext {
|
|||||||
customDialog: CustomDialogService;
|
customDialog: CustomDialogService;
|
||||||
resourceService: ResourceService;
|
resourceService: ResourceService;
|
||||||
userSettingsService: UserSettingsService;
|
userSettingsService: UserSettingsService;
|
||||||
|
utilsService: UtilsService;
|
||||||
telemetryWsService: TelemetryWebsocketService;
|
telemetryWsService: TelemetryWebsocketService;
|
||||||
telemetrySubscribers?: TelemetrySubscriber[];
|
telemetrySubscribers?: TelemetrySubscriber[];
|
||||||
date: DatePipe;
|
date: DatePipe;
|
||||||
|
|||||||
@ -6749,6 +6749,15 @@
|
|||||||
"bar-width": "Bar width",
|
"bar-width": "Bar width",
|
||||||
"bar-width-relative": "Percentage of time window",
|
"bar-width-relative": "Percentage of time window",
|
||||||
"bar-width-absolute": "Absolute (ms)",
|
"bar-width-absolute": "Absolute (ms)",
|
||||||
|
"comparison": {
|
||||||
|
"comparison": "Comparison",
|
||||||
|
"show": "Show",
|
||||||
|
"settings": "Comparison settings",
|
||||||
|
"show-values-for-comparison": "Show historical data for comparison",
|
||||||
|
"comparison-values-label": "Comparison key label",
|
||||||
|
"comparison-values-label-auto": "Auto",
|
||||||
|
"comparison-data-color": "Comparison data color"
|
||||||
|
},
|
||||||
"threshold": {
|
"threshold": {
|
||||||
"thresholds": "Thresholds",
|
"thresholds": "Thresholds",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
@ -6802,6 +6811,7 @@
|
|||||||
"x-axis": "X axis",
|
"x-axis": "X axis",
|
||||||
"y-axis": "Y axis",
|
"y-axis": "Y axis",
|
||||||
"y-axis-settings": "Y axis settings",
|
"y-axis-settings": "Y axis settings",
|
||||||
|
"comparison-x-axis-settings": "Comparison X axis settings",
|
||||||
"remove-y-axis": "Remove Y axis",
|
"remove-y-axis": "Remove Y axis",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
|
|||||||
@ -216,6 +216,18 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
&.flex-xs {
|
||||||
|
@media #{$mat-xs} {
|
||||||
|
width: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.flex-lt-md {
|
||||||
|
@media #{$mat-lt-md} {
|
||||||
|
width: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.fixed-title-width {
|
.fixed-title-width {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user