Timewindow: move datapoints limit to a separate component
This commit is contained in:
parent
37ac8cac44
commit
2e52c50b80
@ -337,6 +337,7 @@ import * as RuleChainSelectComponent from '@shared/components/rule-chain/rule-ch
|
|||||||
import { IModulesMap } from '@modules/common/modules-map.models';
|
import { IModulesMap } from '@modules/common/modules-map.models';
|
||||||
import { TimezoneComponent } from '@shared/components/time/timezone.component';
|
import { TimezoneComponent } from '@shared/components/time/timezone.component';
|
||||||
import { TimezonePanelComponent } from '@shared/components/time/timezone-panel.component';
|
import { TimezonePanelComponent } from '@shared/components/time/timezone-panel.component';
|
||||||
|
import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component';
|
||||||
|
|
||||||
declare const System;
|
declare const System;
|
||||||
|
|
||||||
@ -470,6 +471,7 @@ class ModulesMap implements IModulesMap {
|
|||||||
'@shared/components/time/timezone-select.component': TimezoneSelectComponent,
|
'@shared/components/time/timezone-select.component': TimezoneSelectComponent,
|
||||||
'@shared/components/time/timezone.component': TimezoneComponent,
|
'@shared/components/time/timezone.component': TimezoneComponent,
|
||||||
'@shared/components/time/timezone-panel.component': TimezonePanelComponent,
|
'@shared/components/time/timezone-panel.component': TimezonePanelComponent,
|
||||||
|
'@shared/components/time/datapoints-limit': DatapointsLimitComponent,
|
||||||
'@shared/components/value-input.component': ValueInputComponent,
|
'@shared/components/value-input.component': ValueInputComponent,
|
||||||
'@shared/components/dashboard-autocomplete.component': DashboardAutocompleteComponent,
|
'@shared/components/dashboard-autocomplete.component': DashboardAutocompleteComponent,
|
||||||
'@shared/components/entity/entity-subtype-autocomplete.component': EntitySubTypeAutocompleteComponent,
|
'@shared/components/entity/entity-subtype-autocomplete.component': EntitySubTypeAutocompleteComponent,
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
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]="datapointsLimitFormGroup" class="limit-slider-container" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
<mat-slider fxFlex
|
||||||
|
min="{{minDatapointsLimit()}}"
|
||||||
|
max="{{maxDatapointsLimit()}}">
|
||||||
|
<input matSliderThumb formControlName="limit" [value]="datapointsLimitFormGroup.get('limit').value"/>
|
||||||
|
</mat-slider>
|
||||||
|
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic" appearance="outline">
|
||||||
|
<input matInput formControlName="limit" type="number" step="1"
|
||||||
|
[value]="datapointsLimitFormGroup.get('limit').value"
|
||||||
|
min="{{minDatapointsLimit()}}"
|
||||||
|
max="{{maxDatapointsLimit()}}"/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
@ -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.
|
||||||
|
*/
|
||||||
|
@import "../../../../scss/constants";
|
||||||
|
|
||||||
|
.limit-slider-container {
|
||||||
|
width: 100%;
|
||||||
|
.limit-slider-value {
|
||||||
|
margin-left: 16px;
|
||||||
|
min-width: 25px;
|
||||||
|
max-width: 106px;
|
||||||
|
}
|
||||||
|
mat-form-field input[type=number] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media #{$mat-gt-sm} {
|
||||||
|
.limit-slider-container {
|
||||||
|
> label {
|
||||||
|
margin-right: 16px;
|
||||||
|
width: min-content;
|
||||||
|
max-width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,138 @@
|
|||||||
|
///
|
||||||
|
/// 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, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
|
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||||
|
import { getTimezoneInfo } from '@shared/models/time/time.models';
|
||||||
|
import { TimeService } from '@core/services/time.service';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-datapoints-limit',
|
||||||
|
templateUrl: './datapoints-limit.component.html',
|
||||||
|
styleUrls: ['./datapoints-limit.component.scss'],
|
||||||
|
providers: [{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => DatapointsLimitComponent),
|
||||||
|
multi: true
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
export class DatapointsLimitComponent implements ControlValueAccessor, OnInit, OnDestroy {
|
||||||
|
|
||||||
|
datapointsLimitFormGroup: UntypedFormGroup;
|
||||||
|
|
||||||
|
modelValue: number | null;
|
||||||
|
|
||||||
|
private requiredValue: boolean;
|
||||||
|
get required(): boolean {
|
||||||
|
return this.requiredValue;
|
||||||
|
}
|
||||||
|
@Input()
|
||||||
|
set required(value: boolean) {
|
||||||
|
const newVal = coerceBooleanProperty(value);
|
||||||
|
if (this.requiredValue !== newVal) {
|
||||||
|
this.requiredValue = newVal;
|
||||||
|
this.updateValidators();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
private propagateChange = (v: any) => { };
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder,
|
||||||
|
private timeService: TimeService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.datapointsLimitFormGroup = this.fb.group({
|
||||||
|
limit: [null]
|
||||||
|
});
|
||||||
|
this.datapointsLimitFormGroup.get('limit').valueChanges.pipe(
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe((value) => {
|
||||||
|
this.updateView(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateValidators() {
|
||||||
|
if (this.datapointsLimitFormGroup) {
|
||||||
|
this.datapointsLimitFormGroup.get('limit').setValidators(this.required
|
||||||
|
? [Validators.required, Validators.min(this.minDatapointsLimit()),
|
||||||
|
Validators.max(this.maxDatapointsLimit())]
|
||||||
|
: []);
|
||||||
|
this.datapointsLimitFormGroup.get('limit').updateValueAndValidity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
if (this.disabled) {
|
||||||
|
this.datapointsLimitFormGroup.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.datapointsLimitFormGroup.enable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkLimit(limit?: number): number {
|
||||||
|
if (!limit || limit < this.minDatapointsLimit()) {
|
||||||
|
return this.minDatapointsLimit();
|
||||||
|
} else if (limit > this.maxDatapointsLimit()) {
|
||||||
|
return this.maxDatapointsLimit();
|
||||||
|
}
|
||||||
|
return limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: number | null): void {
|
||||||
|
this.modelValue = this.checkLimit(value);
|
||||||
|
this.datapointsLimitFormGroup.patchValue(
|
||||||
|
{ limit: this.modelValue }, {emitEvent: false}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateView(value: number | null) {
|
||||||
|
if (this.modelValue !== value) {
|
||||||
|
this.modelValue = value;
|
||||||
|
this.propagateChange(this.modelValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
minDatapointsLimit() {
|
||||||
|
return this.timeService.getMinDatapointsLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
maxDatapointsLimit() {
|
||||||
|
return this.timeService.getMaxDatapointsLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -173,19 +173,11 @@
|
|||||||
<mat-slide-toggle class="mat-slide" formControlName="hideAggInterval">
|
<mat-slide-toggle class="mat-slide" formControlName="hideAggInterval">
|
||||||
{{ 'timewindow.hide' | translate }}
|
{{ 'timewindow.hide' | translate }}
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
<div formGroupName="aggregation" class="limit-slider-container" fxLayout="row" fxLayoutAlign="start center">
|
<ng-container formGroupName="aggregation">
|
||||||
<mat-slider fxFlex
|
<tb-datapoints-limit formControlName="limit"
|
||||||
min="{{minDatapointsLimit()}}"
|
[required]="timewindowForm.get('aggregation.type').value === aggregationTypes.NONE">
|
||||||
max="{{maxDatapointsLimit()}}">
|
</tb-datapoints-limit>
|
||||||
<input matSliderThumb formControlName="limit"/>
|
</ng-container>
|
||||||
</mat-slider>
|
|
||||||
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic" appearance="outline">
|
|
||||||
<input matInput formControlName="limit" type="number" step="1"
|
|
||||||
[value]="timewindowForm.get('aggregation.limit').value"
|
|
||||||
min="{{minDatapointsLimit()}}"
|
|
||||||
max="{{maxDatapointsLimit()}}"/>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -191,7 +191,7 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On
|
|||||||
}),
|
}),
|
||||||
aggregation: this.fb.group({
|
aggregation: this.fb.group({
|
||||||
type: [ isDefined(aggregation?.type) ? this.timewindow.aggregation.type : null ],
|
type: [ isDefined(aggregation?.type) ? this.timewindow.aggregation.type : null ],
|
||||||
limit: [ isDefined(aggregation?.limit) ? this.checkLimit(this.timewindow.aggregation.limit) : null ]
|
limit: [ isDefined(aggregation?.limit) ? this.timewindow.aggregation.limit : null ]
|
||||||
}),
|
}),
|
||||||
timezone: [ isDefined(this.timewindow.timezone) ? this.timewindow.timezone : null ],
|
timezone: [ isDefined(this.timewindow.timezone) ? this.timewindow.timezone : null ],
|
||||||
hideAggregation: [ isDefinedAndNotNull(this.timewindow.hideAggregation)
|
hideAggregation: [ isDefinedAndNotNull(this.timewindow.hideAggregation)
|
||||||
@ -292,15 +292,6 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On
|
|||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkLimit(limit?: number): number {
|
|
||||||
if (!limit || limit < this.minDatapointsLimit()) {
|
|
||||||
return this.minDatapointsLimit();
|
|
||||||
} else if (limit > this.maxDatapointsLimit()) {
|
|
||||||
return this.maxDatapointsLimit();
|
|
||||||
}
|
|
||||||
return limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateValidators(aggType: AggregationType) {
|
private updateValidators(aggType: AggregationType) {
|
||||||
if (aggType !== AggregationType.NONE) {
|
if (aggType !== AggregationType.NONE) {
|
||||||
this.timewindowForm.get('aggregation.limit').clearValidators();
|
this.timewindowForm.get('aggregation.limit').clearValidators();
|
||||||
|
|||||||
@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
@import "../../../../scss/constants";
|
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@ -32,32 +31,10 @@
|
|||||||
|
|
||||||
tb-timeinterval,
|
tb-timeinterval,
|
||||||
tb-quick-time-interval,
|
tb-quick-time-interval,
|
||||||
tb-datetime-period {
|
tb-datetime-period,
|
||||||
|
tb-datapoints-limit {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.limit-slider-container {
|
|
||||||
width: 100%;
|
|
||||||
.limit-slider-value {
|
|
||||||
margin-left: 16px;
|
|
||||||
min-width: 25px;
|
|
||||||
max-width: 106px;
|
|
||||||
}
|
|
||||||
mat-form-field input[type=number] {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media #{$mat-gt-sm} {
|
|
||||||
.limit-slider-container {
|
|
||||||
> label {
|
|
||||||
margin-right: 16px;
|
|
||||||
width: min-content;
|
|
||||||
max-width: 40%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -137,19 +137,9 @@
|
|||||||
<section class="tb-form-row column-xs space-between same-padding"
|
<section class="tb-form-row column-xs space-between same-padding"
|
||||||
*ngIf="timewindowForm.get('aggregation.type').value === aggregationTypes.NONE && (isEdit || !timewindow.hideAggInterval)">
|
*ngIf="timewindowForm.get('aggregation.type').value === aggregationTypes.NONE && (isEdit || !timewindow.hideAggInterval)">
|
||||||
<div>{{ 'aggregation.limit' | translate }}</div>
|
<div>{{ 'aggregation.limit' | translate }}</div>
|
||||||
<div class="limit-slider-container" fxLayout="row" fxLayoutAlign="start center">
|
<tb-datapoints-limit formControlName="limit"
|
||||||
<mat-slider fxFlex
|
[required]="timewindowForm.get('aggregation.type').value === aggregationTypes.NONE">
|
||||||
min="{{minDatapointsLimit()}}"
|
</tb-datapoints-limit>
|
||||||
max="{{maxDatapointsLimit()}}">
|
|
||||||
<input matSliderThumb formControlName="limit"/>
|
|
||||||
</mat-slider>
|
|
||||||
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic" appearance="outline">
|
|
||||||
<input matInput formControlName="limit" type="number" step="1"
|
|
||||||
[value]="timewindowForm.get('aggregation.limit').value"
|
|
||||||
min="{{minDatapointsLimit()}}"
|
|
||||||
max="{{maxDatapointsLimit()}}"/>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|||||||
@ -275,7 +275,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
|
|||||||
disabled: hideAggregation
|
disabled: hideAggregation
|
||||||
}],
|
}],
|
||||||
limit: [{
|
limit: [{
|
||||||
value: isDefined(aggregation?.limit) ? this.checkLimit(aggregation.limit) : null,
|
value: isDefined(aggregation?.limit) ? aggregation.limit : null,
|
||||||
disabled: hideAggInterval
|
disabled: hideAggInterval
|
||||||
}, []]
|
}, []]
|
||||||
}),
|
}),
|
||||||
@ -303,15 +303,6 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
|
|||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkLimit(limit?: number): number {
|
|
||||||
if (!limit || limit < this.minDatapointsLimit()) {
|
|
||||||
return this.minDatapointsLimit();
|
|
||||||
} else if (limit > this.maxDatapointsLimit()) {
|
|
||||||
return this.maxDatapointsLimit();
|
|
||||||
}
|
|
||||||
return limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateValidators(aggType: AggregationType) {
|
private updateValidators(aggType: AggregationType) {
|
||||||
if (aggType !== AggregationType.NONE) {
|
if (aggType !== AggregationType.NONE) {
|
||||||
this.timewindowForm.get('aggregation.limit').clearValidators();
|
this.timewindowForm.get('aggregation.limit').clearValidators();
|
||||||
|
|||||||
@ -228,6 +228,7 @@ import { ScadaSymbolInputComponent } from '@shared/components/image/scada-symbol
|
|||||||
import { CountryAutocompleteComponent } from '@shared/components/country-autocomplete.component';
|
import { CountryAutocompleteComponent } from '@shared/components/country-autocomplete.component';
|
||||||
import { CountryData } from '@shared/models/country.models';
|
import { CountryData } from '@shared/models/country.models';
|
||||||
import { SvgXmlComponent } from '@shared/components/svg-xml.component';
|
import { SvgXmlComponent } from '@shared/components/svg-xml.component';
|
||||||
|
import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component';
|
||||||
|
|
||||||
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
||||||
return markedOptionsService;
|
return markedOptionsService;
|
||||||
@ -317,6 +318,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
|||||||
TimezoneComponent,
|
TimezoneComponent,
|
||||||
TimezonePanelComponent,
|
TimezonePanelComponent,
|
||||||
QuickTimeIntervalComponent,
|
QuickTimeIntervalComponent,
|
||||||
|
DatapointsLimitComponent,
|
||||||
DashboardSelectComponent,
|
DashboardSelectComponent,
|
||||||
DashboardSelectPanelComponent,
|
DashboardSelectPanelComponent,
|
||||||
DatetimePeriodComponent,
|
DatetimePeriodComponent,
|
||||||
@ -529,6 +531,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
|||||||
TimezoneComponent,
|
TimezoneComponent,
|
||||||
TimezonePanelComponent,
|
TimezonePanelComponent,
|
||||||
QuickTimeIntervalComponent,
|
QuickTimeIntervalComponent,
|
||||||
|
DatapointsLimitComponent,
|
||||||
DashboardSelectComponent,
|
DashboardSelectComponent,
|
||||||
DatetimePeriodComponent,
|
DatetimePeriodComponent,
|
||||||
DatetimeComponent,
|
DatetimeComponent,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user