10 changed files with 223 additions and 71 deletions
@ -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(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue