Browse Source

UI: timeinterval component redesign

pull/11633/head
Chantsova Ekaterina 2 years ago
parent
commit
399bfb8dbd
  1. 54
      ui-ngx/src/app/shared/components/time/timeinterval.component.html
  2. 17
      ui-ngx/src/app/shared/components/time/timeinterval.component.scss
  3. 224
      ui-ngx/src/app/shared/components/time/timeinterval.component.ts
  4. 2
      ui-ngx/src/app/shared/components/time/timewindow-panel.component.html

54
ui-ngx/src/app/shared/components/time/timeinterval.component.html

@ -15,43 +15,37 @@
limitations under the License.
-->
<section fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px">
<section fxLayout="column" fxLayoutAlign="start center" [fxShow]="isEdit">
<label class="tb-small hide-label" translate>timewindow.hide</label>
<mat-checkbox [(ngModel)]="hideFlag" (ngModelChange)="onHideFlagChange()"></mat-checkbox>
</section>
<section class="interval-section" fxLayout="column" fxFlex fxLayoutGap="16px">
<section fxLayout="row" fxFlex [fxShow]="isEdit || !hideFlag">
<mat-form-field fxFlex [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label *ngIf="predefinedName" translate>{{ predefinedName }}</mat-label>
<mat-select [disabled]="hideFlag || disabled" [(ngModel)]="interval" (ngModelChange)="onIntervalChange()" style="min-width: 150px;">
<mat-option *ngFor="let interval of intervals" [value]="interval.value">
{{ interval.name | translate:interval.translateParams }}
</mat-option>
</mat-select>
</mat-form-field>
</section>
<section fxLayout="row wrap" fxLayoutAlign="start start" fxFlex fxLayoutGap="6px" [fxShow]="advanced && (isEdit || !hideFlag)">
<mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<section class="tb-form-panel no-border no-padding" [formGroup]="timeintervalFormGroup">
<mat-form-field fxFlex [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label *ngIf="predefinedName" translate>{{ predefinedName }}</mat-label>
<mat-select formControlName="interval">
<mat-option *ngFor="let interval of intervals" [value]="interval.value">
{{ interval.name | translate:interval.translateParams }}
</mat-option>
</mat-select>
</mat-form-field>
<section class="tb-form-row column-xs no-border no-padding tb-standard-fields advanced-input"
formGroupName="customInterval"
[fxShow]="advanced">
<div class="tb-flex">
<mat-form-field class="number-input flex" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.days</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" min="0" [(ngModel)]="days" (ngModelChange)="onTimeInputChange('days')"/>
<input matInput type="number" step="1" min="0" formControlName="days" />
</mat-form-field>
<mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-form-field class="number-input flex" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.hours</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="hours" (ngModelChange)="onTimeInputChange('hours')"/>
<input matInput type="number" step="1" formControlName="hours" />
</mat-form-field>
<mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
</div>
<div class="tb-flex">
<mat-form-field class="number-input flex" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.minutes</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="mins" (ngModelChange)="onTimeInputChange('mins')"/>
<input matInput type="number" step="1" formControlName="mins" />
</mat-form-field>
<mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-form-field class="number-input flex" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.seconds</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="secs" (ngModelChange)="onTimeInputChange('secs')"/>
<input matInput type="number" step="1" formControlName="secs" />
</mat-form-field>
</section>
</section>
<section fxLayout="column" fxLayoutAlign="center center" [fxShow]="(isEdit || !hideFlag) && !disabledAdvanced">
<label class="tb-small advanced-label" translate>timeinterval.advanced</label>
<mat-slide-toggle [disabled]="hideFlag || disabled" class="advanced-switch" [(ngModel)]="advanced" (ngModelChange)="onAdvancedChange()"></mat-slide-toggle>
</div>
</section>
</section>

17
ui-ngx/src/app/shared/components/time/timeinterval.component.scss

@ -18,22 +18,21 @@
:host {
min-width: 355px;
.advanced-switch {
margin-bottom: 16px;
.tb-form-panel {
gap: 12px;
}
.advanced-label {
margin: 5px 0;
}
.hide-label {
margin-bottom: 5px;
margin-right: 5px;
.advanced-input {
gap: 8px;
}
@media #{$mat-xs} {
min-width: 0;
width: 100%;
.advanced-input {
gap: 12px;
}
}
}

224
ui-ngx/src/app/shared/components/time/timeinterval.component.ts

@ -14,8 +14,8 @@
/// limitations under the License.
///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { TimeService } from '@core/services/time.service';
import { coerceNumberProperty } from '@angular/cdk/coercion';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
@ -23,6 +23,8 @@ import { coerceBoolean } from '@shared/decorators/coercion';
import { Interval, IntervalMath, TimeInterval } from '@shared/models/time/time.models';
import { isDefined } from '@core/utils';
import { IntervalType } from '@shared/models/telemetry/telemetry.models';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'tb-timeinterval',
@ -36,7 +38,7 @@ import { IntervalType } from '@shared/models/telemetry/telemetry.models';
}
]
})
export class TimeintervalComponent implements OnInit, ControlValueAccessor {
export class TimeintervalComponent implements OnInit, ControlValueAccessor, OnDestroy {
minValue: number;
maxValue: number;
@ -67,10 +69,6 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
@coerceBoolean()
isEdit = false;
@Input()
@coerceBoolean()
hideFlag = false;
@Input()
@coerceBoolean()
disabledAdvanced = false;
@ -79,8 +77,6 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
@coerceBoolean()
useCalendarIntervals = false;
@Output() hideFlagChange = new EventEmitter<boolean>();
@Input() disabled: boolean;
@Input()
@ -89,16 +85,12 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
@Input()
appearance: MatFormFieldAppearance = 'fill';
days = 0;
hours = 0;
mins = 1;
secs = 0;
interval: Interval = 0;
intervals: Array<TimeInterval>;
advanced = false;
timeintervalFormGroup: FormGroup;
customTimeInterval: TimeInterval = {
name: 'timeinterval.custom',
translateParams: {},
@ -113,7 +105,43 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
this.propagateChangeValue = value;
};
constructor(private timeService: TimeService) {
private destroy$ = new Subject<void>();
constructor(private timeService: TimeService,
private fb: FormBuilder) {
this.timeintervalFormGroup = this.fb.group({
interval: [ 1 ],
customInterval: this.fb.group({
days: [ 0 ],
hours: [ 0 ],
mins: [ 1 ],
secs: [ 0 ]
})
});
this.timeintervalFormGroup.get('interval').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.onIntervalChange());
this.timeintervalFormGroup.get('customInterval').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateView());
this.timeintervalFormGroup.get('customInterval.secs').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((secs) => this.onSecsChange(secs));
this.timeintervalFormGroup.get('customInterval.mins').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((mins) => this.onMinsChange(mins));
this.timeintervalFormGroup.get('customInterval.hours').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((hours) => this.onHoursChange(hours));
this.timeintervalFormGroup.get('customInterval.days').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((days) => this.onDaysChange(days));
}
ngOnInit(): void {
@ -132,6 +160,11 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.timeintervalFormGroup.disable({emitEvent: false});
} else {
this.timeintervalFormGroup.enable({emitEvent: false});
}
}
writeValue(interval: Interval): void {
@ -156,20 +189,31 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
}
private setInterval(interval: Interval) {
// if (!this.advanced) {
this.interval = interval;
// }
if (!this.advanced) {
this.timeintervalFormGroup.get('interval').patchValue(interval);
} else {
this.timeintervalFormGroup.get('interval').patchValue(IntervalType.CUSTOM);
this.setCustomInterval(interval);
}
}
private setCustomInterval(interval: Interval) {
const intervalSeconds = Math.floor(IntervalMath.numberValue(interval) / 1000);
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;
this.timeintervalFormGroup.get('customInterval').patchValue({
days: Math.floor(intervalSeconds / 86400),
hours: Math.floor((intervalSeconds % 86400) / 3600),
mins: Math.floor(((intervalSeconds % 86400) % 3600) / 60),
secs: intervalSeconds % 60
});
}
private boundInterval(updateToPreferred = false) {
const min = this.timeService.boundMinInterval(this.minValue);
const max = this.timeService.boundMaxInterval(this.maxValue);
this.intervals = this.timeService.getIntervals(this.minValue, this.maxValue, this.useCalendarIntervals);
if (!this.disabledAdvanced) {
this.intervals.push(this.customTimeInterval);
}
if (this.rendered) {
let newInterval = this.modelValue;
const newIntervalMs = IntervalMath.numberValue(newInterval);
@ -195,7 +239,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
let value: Interval = null;
let interval: Interval;
if (!this.advanced) {
interval = this.interval;
interval = this.timeintervalFormGroup.get('interval').value;
if (!interval || typeof interval === 'number' && isNaN(interval)) {
interval = this.calculateIntervalMs();
}
@ -211,118 +255,90 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor {
}
private calculateIntervalMs(): number {
return (this.days * 86400 +
this.hours * 3600 +
this.mins * 60 +
this.secs) * 1000;
const customInterval = this.timeintervalFormGroup.get('customInterval').value;
return (customInterval.days * 86400 +
customInterval.hours * 3600 +
customInterval.mins * 60 +
customInterval.secs) * 1000;
}
onIntervalChange() {
this.updateView();
}
onAdvancedChange() {
if (!this.advanced) {
this.interval = this.calculateIntervalMs();
} else {
let interval = this.interval;
if (!interval || typeof interval === 'number' && isNaN(interval)) {
interval = this.calculateIntervalMs();
const customIntervalSelected = this.timeintervalFormGroup.get('interval').value === IntervalType.CUSTOM;
if (customIntervalSelected !== this.advanced) {
this.advanced = customIntervalSelected;
if (this.advanced) {
this.setCustomInterval(this.modelValue);
}
this.setInterval(interval);
}
this.updateView();
}
onHideFlagChange() {
this.hideFlagChange.emit(this.hideFlag);
}
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;
}
}
private onSecsChange() {
if (typeof this.secs === 'undefined') {
private onSecsChange(secs: number) {
const customInterval = this.timeintervalFormGroup.get('customInterval').value;
if (typeof secs === 'undefined') {
return;
}
if (this.secs < 0) {
if ((this.days + this.hours + this.mins) > 0) {
this.secs = this.secs + 60;
this.mins--;
this.onMinsChange();
if (secs < 0) {
if ((customInterval.days + customInterval.hours + customInterval.mins) > 0) {
this.timeintervalFormGroup.get('customInterval.secs').patchValue(secs + 60, {emitEvent: false});
this.timeintervalFormGroup.get('customInterval.mins').patchValue(customInterval.mins - 1, {emitEvent: true});
} else {
this.secs = 0;
this.timeintervalFormGroup.get('customInterval.secs').patchValue(0, {emitEvent: false});
}
} else if (this.secs >= 60) {
this.secs = this.secs - 60;
this.mins++;
this.onMinsChange();
} else if (secs >= 60) {
this.timeintervalFormGroup.get('customInterval.secs').patchValue(secs - 60, {emitEvent: false});
this.timeintervalFormGroup.get('customInterval.mins').patchValue(customInterval.mins + 1, {emitEvent: true});
}
this.updateView();
}
private onMinsChange() {
if (typeof this.mins === 'undefined') {
private onMinsChange(mins: number) {
const customInterval = this.timeintervalFormGroup.get('customInterval').value;
if (typeof mins === 'undefined') {
return;
}
if (this.mins < 0) {
if ((this.days + this.hours) > 0) {
this.mins = this.mins + 60;
this.hours--;
this.onHoursChange();
if (mins < 0) {
if ((customInterval.days + customInterval.hours) > 0) {
this.timeintervalFormGroup.get('customInterval.mins').patchValue(mins + 60, {emitEvent: false});
this.timeintervalFormGroup.get('customInterval.hours').patchValue(customInterval.hours - 1, {emitEvent: true});
} else {
this.mins = 0;
this.timeintervalFormGroup.get('customInterval.mins').patchValue(0, {emitEvent: false});
}
} else if (this.mins >= 60) {
this.mins = this.mins - 60;
this.hours++;
this.onHoursChange();
} else if (mins >= 60) {
this.timeintervalFormGroup.get('customInterval.mins').patchValue(mins - 60, {emitEvent: false});
this.timeintervalFormGroup.get('customInterval.hours').patchValue(customInterval.hours + 1, {emitEvent: true});
}
this.updateView();
}
private onHoursChange() {
if (typeof this.hours === 'undefined') {
private onHoursChange(hours: number) {
const customInterval = this.timeintervalFormGroup.get('customInterval').value;
if (typeof hours === 'undefined') {
return;
}
if (this.hours < 0) {
if (this.days > 0) {
this.hours = this.hours + 24;
this.days--;
this.onDaysChange();
if (hours < 0) {
if (customInterval.days > 0) {
this.timeintervalFormGroup.get('customInterval.hours').patchValue(hours + 24, {emitEvent: false});
this.timeintervalFormGroup.get('customInterval.days').patchValue(customInterval.days - 1, {emitEvent: true});
} else {
this.hours = 0;
this.timeintervalFormGroup.get('customInterval.hours').patchValue(0, {emitEvent: false});
}
} else if (this.hours >= 24) {
this.hours = this.hours - 24;
this.days++;
this.onDaysChange();
} else if (hours >= 24) {
this.timeintervalFormGroup.get('customInterval.hours').patchValue(hours - 24, {emitEvent: false});
this.timeintervalFormGroup.get('customInterval.days').patchValue(customInterval.days + 1, {emitEvent: true});
}
this.updateView();
}
private onDaysChange() {
if (typeof this.days === 'undefined') {
private onDaysChange(days: number) {
if (typeof days === 'undefined') {
return;
}
if (this.days < 0) {
this.days = 0;
if (days < 0) {
this.timeintervalFormGroup.get('customInterval.days').patchValue(0, {emitEvent: false});
}
this.updateView();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

2
ui-ngx/src/app/shared/components/time/timewindow-panel.component.html

@ -159,7 +159,7 @@
</section>
</ng-container>
<section class="tb-form-panel stroked" *ngIf="(isEdit || !timewindow.hideAggInterval)
<section class="tb-form-panel stroked" [fxShow]="(isEdit || !timewindow.hideAggInterval)
&& timewindowForm.get('aggregation.type').value !== aggregationTypes.NONE">
<div class="tb-form-panel-title">{{ 'aggregation.group-interval' | translate }}</div>
<ng-container formGroupName="realtime" *ngIf="timewindow.selectedTab === timewindowTypes.REALTIME">

Loading…
Cancel
Save