2019-08-12 19:34:23 +03:00
|
|
|
///
|
2024-01-09 10:46:16 +02:00
|
|
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
2019-08-12 19:34:23 +03:00
|
|
|
///
|
|
|
|
|
/// 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.
|
|
|
|
|
///
|
|
|
|
|
|
2020-04-14 11:38:30 +03:00
|
|
|
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
|
|
|
|
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
2024-01-12 15:04:05 +02:00
|
|
|
import { TimeService } from '@core/services/time.service';
|
2023-03-15 16:15:53 +02:00
|
|
|
import { coerceNumberProperty } from '@angular/cdk/coercion';
|
2023-03-07 12:14:49 +02:00
|
|
|
import { SubscriptSizing } from '@angular/material/form-field';
|
2023-05-04 17:46:43 +03:00
|
|
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
2024-01-12 15:04:05 +02:00
|
|
|
import { Interval, IntervalMath, TimeInterval } from '@shared/models/time/time.models';
|
2019-08-12 19:34:23 +03:00
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'tb-timeinterval',
|
|
|
|
|
templateUrl: './timeinterval.component.html',
|
|
|
|
|
styleUrls: ['./timeinterval.component.scss'],
|
|
|
|
|
providers: [
|
|
|
|
|
{
|
|
|
|
|
provide: NG_VALUE_ACCESSOR,
|
|
|
|
|
useExisting: forwardRef(() => TimeintervalComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
export class TimeintervalComponent implements OnInit, ControlValueAccessor {
|
|
|
|
|
|
|
|
|
|
minValue: number;
|
|
|
|
|
maxValue: number;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
set min(min: number) {
|
2023-03-15 13:28:31 +02:00
|
|
|
const minValueData = coerceNumberProperty(min);
|
|
|
|
|
if (typeof minValueData !== 'undefined' && minValueData !== this.minValue) {
|
|
|
|
|
this.minValue = minValueData;
|
2019-09-03 19:31:16 +03:00
|
|
|
this.maxValue = Math.max(this.maxValue, this.minValue);
|
2019-08-12 19:34:23 +03:00
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
set max(max: number) {
|
2023-03-15 13:28:31 +02:00
|
|
|
const maxValueData = coerceNumberProperty(max);
|
|
|
|
|
if (typeof maxValueData !== 'undefined' && maxValueData !== this.maxValue) {
|
|
|
|
|
this.maxValue = maxValueData;
|
2019-09-03 19:31:16 +03:00
|
|
|
this.minValue = Math.min(this.minValue, this.maxValue);
|
2024-01-08 19:34:07 +02:00
|
|
|
this.updateView(true);
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Input() predefinedName: string;
|
2020-02-25 19:11:25 +02:00
|
|
|
|
|
|
|
|
@Input()
|
2023-03-15 16:15:53 +02:00
|
|
|
@coerceBoolean()
|
|
|
|
|
isEdit = false;
|
2020-02-25 19:11:25 +02:00
|
|
|
|
|
|
|
|
@Input()
|
2023-03-15 16:15:53 +02:00
|
|
|
@coerceBoolean()
|
|
|
|
|
hideFlag = false;
|
2023-02-01 16:25:07 +02:00
|
|
|
|
|
|
|
|
@Input()
|
2023-03-15 16:15:53 +02:00
|
|
|
@coerceBoolean()
|
2023-03-20 16:51:42 +02:00
|
|
|
disabledAdvanced = false;
|
2023-01-27 15:47:08 +02:00
|
|
|
|
2024-01-12 15:04:05 +02:00
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
|
|
|
|
useCalendarIntervals = false;
|
|
|
|
|
|
2020-02-25 19:11:25 +02:00
|
|
|
@Output() hideFlagChange = new EventEmitter<boolean>();
|
|
|
|
|
|
2019-08-12 19:34:23 +03:00
|
|
|
@Input() disabled: boolean;
|
|
|
|
|
|
2023-03-07 12:14:49 +02:00
|
|
|
@Input()
|
|
|
|
|
subscriptSizing: SubscriptSizing = 'fixed';
|
|
|
|
|
|
2019-08-12 19:34:23 +03:00
|
|
|
days = 0;
|
|
|
|
|
hours = 0;
|
|
|
|
|
mins = 1;
|
|
|
|
|
secs = 0;
|
|
|
|
|
|
2024-01-12 15:04:05 +02:00
|
|
|
interval: Interval = 0;
|
|
|
|
|
modelValue: Interval;
|
2019-08-12 19:34:23 +03:00
|
|
|
|
|
|
|
|
advanced = false;
|
|
|
|
|
rendered = false;
|
|
|
|
|
|
|
|
|
|
intervals: Array<TimeInterval>;
|
|
|
|
|
|
|
|
|
|
private propagateChange = (_: any) => {};
|
|
|
|
|
|
|
|
|
|
constructor(private timeService: TimeService) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
|
|
|
|
this.boundInterval();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnChange(fn: any): void {
|
|
|
|
|
this.propagateChange = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnTouched(fn: any): void {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setDisabledState(isDisabled: boolean): void {
|
|
|
|
|
this.disabled = isDisabled;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 15:04:05 +02:00
|
|
|
writeValue(interval: Interval): void {
|
|
|
|
|
this.modelValue = interval;
|
2019-08-12 19:34:23 +03:00
|
|
|
this.rendered = true;
|
|
|
|
|
if (typeof this.modelValue !== 'undefined') {
|
|
|
|
|
const min = this.timeService.boundMinInterval(this.minValue);
|
|
|
|
|
const max = this.timeService.boundMaxInterval(this.maxValue);
|
2024-01-12 15:04:05 +02:00
|
|
|
if (IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) {
|
|
|
|
|
this.advanced = !this.timeService.matchesExistingInterval(this.minValue, this.maxValue, this.modelValue, this.useCalendarIntervals);
|
|
|
|
|
this.setInterval(this.modelValue);
|
2019-08-12 19:34:23 +03:00
|
|
|
} else {
|
|
|
|
|
this.boundInterval();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 15:04:05 +02:00
|
|
|
setInterval(interval: Interval) {
|
2019-08-12 19:34:23 +03:00
|
|
|
if (!this.advanced) {
|
2024-01-12 15:04:05 +02:00
|
|
|
this.interval = interval;
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
2024-01-12 15:04:05 +02:00
|
|
|
const intervalSeconds = Math.floor(IntervalMath.numberValue(interval) / 1000);
|
2019-08-12 19:34:23 +03:00
|
|
|
this.days = Math.floor(intervalSeconds / 86400);
|
|
|
|
|
this.hours = Math.floor((intervalSeconds % 86400) / 3600);
|
|
|
|
|
this.mins = Math.floor(((intervalSeconds % 86400) % 3600) / 60);
|
|
|
|
|
this.secs = intervalSeconds % 60;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 19:34:07 +02:00
|
|
|
boundInterval(updateToPreferred = false) {
|
2019-08-12 19:34:23 +03:00
|
|
|
const min = this.timeService.boundMinInterval(this.minValue);
|
|
|
|
|
const max = this.timeService.boundMaxInterval(this.maxValue);
|
2024-01-12 15:04:05 +02:00
|
|
|
this.intervals = this.timeService.getIntervals(this.minValue, this.maxValue, this.useCalendarIntervals);
|
2019-08-12 19:34:23 +03:00
|
|
|
if (this.rendered) {
|
2024-01-12 15:04:05 +02:00
|
|
|
let newInterval = this.modelValue;
|
|
|
|
|
const newIntervalMs = IntervalMath.numberValue(newInterval);
|
2019-08-12 19:34:23 +03:00
|
|
|
if (newIntervalMs < min) {
|
2024-01-12 15:04:05 +02:00
|
|
|
newInterval = min;
|
2024-01-08 19:34:07 +02:00
|
|
|
} else if (newIntervalMs >= max && updateToPreferred) {
|
2024-01-12 15:04:05 +02:00
|
|
|
newInterval = this.timeService.boundMaxInterval(max / 7);
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
|
|
|
|
if (!this.advanced) {
|
2024-01-12 15:04:05 +02:00
|
|
|
newInterval = this.timeService.boundToPredefinedInterval(min, max, newInterval, this.useCalendarIntervals);
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
2024-01-12 15:04:05 +02:00
|
|
|
if (newInterval !== this.modelValue) {
|
|
|
|
|
this.setInterval(newInterval);
|
2019-08-12 19:34:23 +03:00
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 19:34:07 +02:00
|
|
|
updateView(updateToPreferred = false) {
|
2019-08-12 19:34:23 +03:00
|
|
|
if (!this.rendered) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-01-12 15:04:05 +02:00
|
|
|
let value: Interval = null;
|
|
|
|
|
let interval: Interval;
|
2019-08-12 19:34:23 +03:00
|
|
|
if (!this.advanced) {
|
2024-01-12 15:04:05 +02:00
|
|
|
interval = this.interval;
|
|
|
|
|
if (!interval || typeof interval === 'number' && isNaN(interval)) {
|
|
|
|
|
interval = this.calculateIntervalMs();
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
|
|
|
|
} else {
|
2024-01-12 15:04:05 +02:00
|
|
|
interval = this.calculateIntervalMs();
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
2024-01-12 15:04:05 +02:00
|
|
|
if (typeof interval === 'string' || !isNaN(interval) && interval > 0) {
|
|
|
|
|
value = interval;
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
|
|
|
|
this.modelValue = value;
|
|
|
|
|
this.propagateChange(this.modelValue);
|
2024-01-08 19:34:07 +02:00
|
|
|
this.boundInterval(updateToPreferred);
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calculateIntervalMs(): number {
|
|
|
|
|
return (this.days * 86400 +
|
|
|
|
|
this.hours * 3600 +
|
|
|
|
|
this.mins * 60 +
|
|
|
|
|
this.secs) * 1000;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 15:04:05 +02:00
|
|
|
onIntervalChange() {
|
2019-08-12 19:34:23 +03:00
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onAdvancedChange() {
|
|
|
|
|
if (!this.advanced) {
|
2024-01-12 15:04:05 +02:00
|
|
|
this.interval = this.calculateIntervalMs();
|
2019-08-12 19:34:23 +03:00
|
|
|
} else {
|
2024-01-12 15:04:05 +02:00
|
|
|
let interval = this.interval;
|
|
|
|
|
if (!interval || typeof interval === 'number' && isNaN(interval)) {
|
|
|
|
|
interval = this.calculateIntervalMs();
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
2024-01-12 15:04:05 +02:00
|
|
|
this.setInterval(interval);
|
2019-08-12 19:34:23 +03:00
|
|
|
}
|
|
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-25 19:11:25 +02:00
|
|
|
onHideFlagChange() {
|
2023-04-04 12:37:24 +03:00
|
|
|
this.hideFlagChange.emit(this.hideFlag);
|
2020-02-25 19:11:25 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-12 19:34:23 +03:00
|
|
|
onTimeInputChange(type: string) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case 'secs':
|
|
|
|
|
setTimeout(() => this.onSecsChange(), 0);
|
|
|
|
|
break;
|
|
|
|
|
case 'mins':
|
|
|
|
|
setTimeout(() => this.onMinsChange(), 0);
|
|
|
|
|
break;
|
|
|
|
|
case 'hours':
|
|
|
|
|
setTimeout(() => this.onHoursChange(), 0);
|
|
|
|
|
break;
|
|
|
|
|
case 'days':
|
|
|
|
|
setTimeout(() => this.onDaysChange(), 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onSecsChange() {
|
|
|
|
|
if (typeof this.secs === 'undefined') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this.secs < 0) {
|
|
|
|
|
if ((this.days + this.hours + this.mins) > 0) {
|
|
|
|
|
this.secs = this.secs + 60;
|
|
|
|
|
this.mins--;
|
|
|
|
|
this.onMinsChange();
|
|
|
|
|
} else {
|
|
|
|
|
this.secs = 0;
|
|
|
|
|
}
|
|
|
|
|
} else if (this.secs >= 60) {
|
|
|
|
|
this.secs = this.secs - 60;
|
|
|
|
|
this.mins++;
|
|
|
|
|
this.onMinsChange();
|
|
|
|
|
}
|
|
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMinsChange() {
|
|
|
|
|
if (typeof this.mins === 'undefined') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this.mins < 0) {
|
|
|
|
|
if ((this.days + this.hours) > 0) {
|
|
|
|
|
this.mins = this.mins + 60;
|
|
|
|
|
this.hours--;
|
|
|
|
|
this.onHoursChange();
|
|
|
|
|
} else {
|
|
|
|
|
this.mins = 0;
|
|
|
|
|
}
|
|
|
|
|
} else if (this.mins >= 60) {
|
|
|
|
|
this.mins = this.mins - 60;
|
|
|
|
|
this.hours++;
|
|
|
|
|
this.onHoursChange();
|
|
|
|
|
}
|
|
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onHoursChange() {
|
|
|
|
|
if (typeof this.hours === 'undefined') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this.hours < 0) {
|
|
|
|
|
if (this.days > 0) {
|
|
|
|
|
this.hours = this.hours + 24;
|
|
|
|
|
this.days--;
|
|
|
|
|
this.onDaysChange();
|
|
|
|
|
} else {
|
|
|
|
|
this.hours = 0;
|
|
|
|
|
}
|
|
|
|
|
} else if (this.hours >= 24) {
|
|
|
|
|
this.hours = this.hours - 24;
|
|
|
|
|
this.days++;
|
|
|
|
|
this.onDaysChange();
|
|
|
|
|
}
|
|
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onDaysChange() {
|
|
|
|
|
if (typeof this.days === 'undefined') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (this.days < 0) {
|
|
|
|
|
this.days = 0;
|
|
|
|
|
}
|
|
|
|
|
this.updateView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|