UI: Updated shared component

This commit is contained in:
Vladyslav_Prykhodko 2025-05-27 11:31:34 +03:00
parent 43176d37fc
commit e5d6b2bf1b
12 changed files with 176 additions and 99 deletions

View File

@ -21,7 +21,8 @@ import {
Component, Component,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
Input, NgZone, Input,
NgZone,
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
@ -59,7 +60,7 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { AddEntityDialogComponent } from './add-entity-dialog.component'; import { AddEntityDialogComponent } from './add-entity-dialog.component';
import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models';
import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { getTimePageLinkInterval, Timewindow } from '@shared/models/time/time.models';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
import { isDefined, isEqual, isNotEmptyStr, isUndefined } from '@core/utils'; import { isDefined, isEqual, isNotEmptyStr, isUndefined } from '@core/utils';
@ -259,7 +260,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
if (this.entitiesTableConfig.useTimePageLink) { if (this.entitiesTableConfig.useTimePageLink) {
this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval; this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval;
const interval = this.getTimePageLinkInterval(); const interval = getTimePageLinkInterval(this.timewindow);
this.pageLink = new TimePageLink(10, 0, null, sortOrder, this.pageLink = new TimePageLink(10, 0, null, sortOrder,
interval.startTime, interval.endTime); interval.startTime, interval.endTime);
} else { } else {
@ -424,7 +425,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
} }
if (this.entitiesTableConfig.useTimePageLink) { if (this.entitiesTableConfig.useTimePageLink) {
const timePageLink = this.pageLink as TimePageLink; const timePageLink = this.pageLink as TimePageLink;
const interval = this.getTimePageLinkInterval(); const interval = getTimePageLinkInterval(this.timewindow);
timePageLink.startTime = interval.startTime; timePageLink.startTime = interval.startTime;
timePageLink.endTime = interval.endTime; timePageLink.endTime = interval.endTime;
} }
@ -434,31 +435,6 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
} }
} }
private getTimePageLinkInterval(): {startTime?: number; endTime?: number} {
const interval: {startTime?: number; endTime?: number} = {};
switch (this.timewindow.history.historyType) {
case HistoryWindowType.LAST_INTERVAL:
const currentTime = Date.now();
interval.startTime = currentTime - this.timewindow.history.timewindowMs;
interval.endTime = currentTime;
break;
case HistoryWindowType.FIXED:
interval.startTime = this.timewindow.history.fixedTimewindow.startTimeMs;
interval.endTime = this.timewindow.history.fixedTimewindow.endTimeMs;
break;
case HistoryWindowType.INTERVAL:
const startEndTime = calculateIntervalStartEndTime(this.timewindow.history.quickInterval);
interval.startTime = startEndTime[0];
interval.endTime = startEndTime[1];
break;
case HistoryWindowType.FOR_ALL_TIME:
interval.startTime = null;
interval.endTime = null;
break;
}
return interval;
}
private dataLoaded(col?: number, row?: number) { private dataLoaded(col?: number, row?: number) {
if (isFinite(col) && isFinite(row)) { if (isFinite(col) && isFinite(row)) {
this.clearCellCache(col, row); this.clearCellCache(col, row);

View File

@ -17,16 +17,20 @@
--> -->
<div class="tb-entity-list-select flex flex-row" [formGroup]="entityListSelectFormGroup"> <div class="tb-entity-list-select flex flex-row" [formGroup]="entityListSelectFormGroup">
<tb-entity-type-select <tb-entity-type-select
[inlineField]="inlineField"
[class.flex-1]="inlineField && !modelValue.entityType"
style="min-width: 100px; padding-right: 8px;" style="min-width: 100px; padding-right: 8px;"
*ngIf="displayEntityTypeSelect" *ngIf="displayEntityTypeSelect"
[showLabel]="true" [showLabel]="true"
[required]="required" [required]="required"
[useAliasEntityTypes]="useAliasEntityTypes" [useAliasEntityTypes]="useAliasEntityTypes"
[allowedEntityTypes]="allowedEntityTypes" [allowedEntityTypes]="allowedEntityTypes"
[filterAllowedEntityTypes]="filterAllowedEntityTypes"
formControlName="entityType"> formControlName="entityType">
</tb-entity-type-select> </tb-entity-type-select>
<tb-entity-list <tb-entity-list
class="flex-1" class="flex-1"
[inlineField]="inlineField"
[class.tb-not-empty]="modelValue.ids?.length > 0" [class.tb-not-empty]="modelValue.ids?.length > 0"
*ngIf="modelValue.entityType" *ngIf="modelValue.entityType"
[required]="required" [required]="required"

View File

@ -14,16 +14,13 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { AfterViewInit, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { booleanAttribute, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
import { EntityService } from '@core/http/entity.service'; import { EntityService } from '@core/http/entity.service';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { isDefinedAndNotNull } from '@core/utils';
interface EntityListSelectModel { interface EntityListSelectModel {
entityType: EntityType | AliasEntityType; entityType: EntityType | AliasEntityType;
@ -41,7 +38,7 @@ interface EntityListSelectModel {
}] }]
}) })
export class EntityListSelectComponent implements ControlValueAccessor, OnInit, AfterViewInit { export class EntityListSelectComponent implements ControlValueAccessor, OnInit {
entityListSelectFormGroup: UntypedFormGroup; entityListSelectFormGroup: UntypedFormGroup;
@ -53,27 +50,28 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit,
@Input() @Input()
useAliasEntityTypes: boolean; useAliasEntityTypes: boolean;
private requiredValue: boolean; @Input({transform: booleanAttribute})
get required(): boolean { required: boolean;
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input() @Input()
disabled: boolean; disabled: boolean;
@Input({transform: booleanAttribute})
inlineField: boolean;
@Input({transform: booleanAttribute})
filterAllowedEntityTypes = true;
@Input()
predefinedEntityType: EntityType | AliasEntityType;
displayEntityTypeSelect: boolean; displayEntityTypeSelect: boolean;
private readonly defaultEntityType: EntityType | AliasEntityType = null; private defaultEntityType: EntityType | AliasEntityType = null;
private propagateChange = (v: any) => { }; private propagateChange = (_v: any) => { };
constructor(private store: Store<AppState>, constructor(private entityService: EntityService,
private entityService: EntityService,
public translate: TranslateService,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) { private destroyRef: DestroyRef) {
@ -96,7 +94,7 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit,
this.propagateChange = fn; this.propagateChange = fn;
} }
registerOnTouched(fn: any): void { registerOnTouched(_fn: any): void {
} }
ngOnInit() { ngOnInit() {
@ -114,9 +112,9 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit,
this.updateView(this.modelValue.entityType, values); this.updateView(this.modelValue.entityType, values);
} }
); );
} if (isDefinedAndNotNull(this.predefinedEntityType)) {
this.defaultEntityType = this.predefinedEntityType;
ngAfterViewInit(): void { }
} }
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
@ -145,7 +143,7 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit,
this.entityListSelectFormGroup.get('entityIds').patchValue([...this.modelValue.ids], {emitEvent: true}); this.entityListSelectFormGroup.get('entityIds').patchValue([...this.modelValue.ids], {emitEvent: true});
} }
updateView(entityType: EntityType | AliasEntityType | null, entityIds: Array<string> | null) { private updateView(entityType: EntityType | AliasEntityType | null, entityIds: Array<string> | null) {
if (this.modelValue.entityType !== entityType || if (this.modelValue.entityType !== entityType ||
!this.compareIds(this.modelValue.ids, entityIds)) { !this.compareIds(this.modelValue.ids, entityIds)) {
this.modelValue = { this.modelValue = {
@ -156,7 +154,7 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit,
} }
} }
compareIds(ids1: Array<string> | null, ids2: Array<string> | null): boolean { private compareIds(ids1: Array<string> | null, ids2: Array<string> | null): boolean {
if (ids1 !== null && ids2 !== null) { if (ids1 !== null && ids2 !== null) {
return JSON.stringify(ids1) === JSON.stringify(ids2); return JSON.stringify(ids1) === JSON.stringify(ids2);
} else { } else {
@ -164,7 +162,7 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit,
} }
} }
toEntityIds(modelValue: EntityListSelectModel): Array<EntityId> { private toEntityIds(modelValue: EntityListSelectModel): Array<EntityId> {
if (modelValue !== null && modelValue.entityType && modelValue.ids && modelValue.ids.length > 0) { if (modelValue !== null && modelValue.entityType && modelValue.ids && modelValue.ids.length > 0) {
const entityType = modelValue.entityType; const entityType = modelValue.entityType;
return modelValue.ids.map(id => ({entityType, id})); return modelValue.ids.map(id => ({entityType, id}));

View File

@ -15,8 +15,13 @@
limitations under the License. limitations under the License.
--> -->
<mat-form-field [formGroup]="entityListFormGroup" class="mat-block" [class.tb-chip-list]="!labelText" [appearance]="appearance" [subscriptSizing]="subscriptSizing"> <mat-form-field [formGroup]="entityListFormGroup" class="mat-block"
<mat-label *ngIf="labelText">{{ labelText }}</mat-label> [appearance]="inlineField ? 'outline' : appearance"
[class.tb-chip-list]="!labelText && !inlineField"
[class.tb-chips]="inlineField"
[class.flex]="inlineField"
[subscriptSizing]="inlineField ? 'dynamic' : subscriptSizing">
<mat-label *ngIf="!inlineField && labelText">{{ labelText }}</mat-label> <mat-label *ngIf="labelText">{{ labelText }}</mat-label>
<mat-chip-grid #chipList formControlName="entities"> <mat-chip-grid #chipList formControlName="entities">
<mat-chip-row <mat-chip-row
*ngFor="let entity of entities" *ngFor="let entity of entities"
@ -48,16 +53,16 @@
</div> </div>
<ng-template #searchNotEmpty> <ng-template #searchNotEmpty>
<span> <span>
{{ translate.get('entity.no-entities-matching', {entity: searchText}) | async }} {{ 'entity.no-entities-matching' | translate: {entity: searchText} }}
</span> </span>
</ng-template> </ng-template>
</div> </div>
</mat-option> </mat-option>
</mat-autocomplete> </mat-autocomplete>
<mat-hint *ngIf="hint"> <mat-hint *ngIf="!inlineField && hint">
{{ hint }} {{ hint }}
</mat-hint> </mat-hint>
<mat-error *ngIf="entityListFormGroup.get('entities').hasError('required')"> <mat-error *ngIf="!inlineField && entityListFormGroup.get('entities').hasError('required')">
{{ requiredText }} {{ requiredText }}
</mat-error> </mat-error>
<div matSuffix> <div matSuffix>

View File

@ -14,17 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { import { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
AfterViewInit,
Component,
ElementRef,
forwardRef,
Input,
OnChanges,
OnInit,
SimpleChanges,
ViewChild
} from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALIDATORS, NG_VALIDATORS,
@ -65,7 +55,7 @@ import { isArray } from 'lodash';
} }
] ]
}) })
export class EntityListComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnChanges { export class EntityListComponent implements ControlValueAccessor, OnInit, OnChanges {
entityListFormGroup: UntypedFormGroup; entityListFormGroup: UntypedFormGroup;
@ -115,6 +105,10 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
@coerceBoolean() @coerceBoolean()
syncIdsWithDB = false; syncIdsWithDB = false;
@Input()
@coerceBoolean()
inlineField: boolean;
@ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>; @ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>;
@ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete; @ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete;
@ViewChild('chipList', {static: true}) chipList: MatChipGrid; @ViewChild('chipList', {static: true}) chipList: MatChipGrid;
@ -126,9 +120,9 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
private dirty = false; private dirty = false;
private propagateChange = (v: any) => { }; private propagateChange = (_v: any) => { };
constructor(public translate: TranslateService, constructor(private translate: TranslateService,
private entityService: EntityService, private entityService: EntityService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder) {
this.entityListFormGroup = this.fb.group({ this.entityListFormGroup = this.fb.group({
@ -146,7 +140,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
this.propagateChange = fn; this.propagateChange = fn;
} }
registerOnTouched(fn: any): void { registerOnTouched(_fn: any): void {
} }
ngOnInit() { ngOnInit() {
@ -178,9 +172,6 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
} }
} }
ngAfterViewInit(): void {
}
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (isDisabled) { if (isDisabled) {

View File

@ -15,14 +15,16 @@
limitations under the License. limitations under the License.
--> -->
<mat-form-field [formGroup]="entityTypeFormGroup" [appearance]="appearance"> <mat-form-field [formGroup]="entityTypeFormGroup"
<mat-label *ngIf="showLabel">{{ 'entity.type' | translate }}</mat-label> [subscriptSizing]="inlineField ? 'dynamic' : 'fixed'"
[appearance]="inlineField ? 'outline' : appearance">
<mat-label *ngIf="showLabel && !inlineField">{{ label }}</mat-label>
<mat-select [required]="required" formControlName="entityType"> <mat-select [required]="required" formControlName="entityType">
<mat-option *ngFor="let type of entityTypes" [value]="type"> <mat-option *ngFor="let type of entityTypes" [value]="type">
{{ displayEntityTypeFn(type) }} {{ displayEntityTypeFn(type) }}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="entityTypeFormGroup.get('entityType').hasError('required')"> <mat-error *ngIf="!inlineField && entityTypeFormGroup.get('entityType').hasError('required')">
{{ 'entity.type-required' | translate }} {{ 'entity.type-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>

View File

@ -52,6 +52,9 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit,
@coerceBoolean() @coerceBoolean()
showLabel: boolean; showLabel: boolean;
@Input()
label = this.translate.instant('entity.type');
@Input() @Input()
@coerceBoolean() @coerceBoolean()
required: boolean; required: boolean;
@ -65,12 +68,16 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit,
@Input() @Input()
appearance: MatFormFieldAppearance = 'fill'; appearance: MatFormFieldAppearance = 'fill';
@Input()
@coerceBoolean()
inlineField: boolean;
entityTypes: Array<EntityType | AliasEntityType | string>; entityTypes: Array<EntityType | AliasEntityType | string>;
private propagateChange = (v: any) => { }; private propagateChange = (_v: any) => { };
constructor(private entityService: EntityService, constructor(private entityService: EntityService,
public translate: TranslateService, private translate: TranslateService,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) { private destroyRef: DestroyRef) {
this.entityTypeFormGroup = this.fb.group({ this.entityTypeFormGroup = this.fb.group({
@ -82,7 +89,7 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit,
this.propagateChange = fn; this.propagateChange = fn;
} }
registerOnTouched(fn: any): void { registerOnTouched(_fn: any): void {
} }
ngOnInit() { ngOnInit() {
@ -97,7 +104,7 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit,
takeUntilDestroyed(this.destroyRef) takeUntilDestroyed(this.destroyRef)
).subscribe( ).subscribe(
(value) => { (value) => {
let modelValue; let modelValue: EntityType | AliasEntityType;
if (!value || value === '') { if (!value || value === '') {
modelValue = null; modelValue = null;
} else { } else {

View File

@ -27,7 +27,7 @@
</button> </button>
</div> </div>
<div class="tb-timewindow-form-content tb-form-panel no-border"> <div class="tb-timewindow-form-content tb-form-panel no-border" [class.no-padding]="!panelMode">
<ng-container *ngIf="timewindowForm.get('selectedTab').value === timewindowTypes.REALTIME"> <ng-container *ngIf="timewindowForm.get('selectedTab').value === timewindowTypes.REALTIME">
<section class="tb-form-panel stroked" *ngIf="realtimeIntervalSelectionAvailable; else timezoneSelectionPanel"> <section class="tb-form-panel stroked" *ngIf="realtimeIntervalSelectionAvailable; else timezoneSelectionPanel">
<div class="tb-flex space-between" <div class="tb-flex space-between"
@ -189,8 +189,8 @@
formControlName="timezone"> formControlName="timezone">
</tb-timezone> </tb-timezone>
</ng-template> </ng-template>
<mat-divider></mat-divider> <mat-divider *ngIf="panelMode"></mat-divider>
<div class="tb-panel-actions tb-flex flex-end no-gap"> <div class="tb-panel-actions tb-flex flex-end no-gap" *ngIf="panelMode">
<button type="button" <button type="button"
mat-button mat-button
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"

View File

@ -14,7 +14,17 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, InjectionToken, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; import {
Component,
EventEmitter,
Inject,
InjectionToken,
OnDestroy,
OnInit,
Optional,
Output,
ViewContainerRef
} from '@angular/core';
import { import {
AggregationType, AggregationType,
currentHistoryTimewindow, currentHistoryTimewindow,
@ -57,6 +67,7 @@ export interface TimewindowPanelData {
aggregation: boolean; aggregation: boolean;
timezone: boolean; timezone: boolean;
isEdit: boolean; isEdit: boolean;
panelMode: boolean;
} }
export const TIMEWINDOW_PANEL_DATA = new InjectionToken<any>('TimewindowPanelData'); export const TIMEWINDOW_PANEL_DATA = new InjectionToken<any>('TimewindowPanelData');
@ -68,6 +79,9 @@ export const TIMEWINDOW_PANEL_DATA = new InjectionToken<any>('TimewindowPanelDat
}) })
export class TimewindowPanelComponent extends PageComponent implements OnInit, OnDestroy { export class TimewindowPanelComponent extends PageComponent implements OnInit, OnDestroy {
@Output()
changeTimewindow = new EventEmitter<Timewindow>();
historyOnly = false; historyOnly = false;
forAllTimeEnabled = false; forAllTimeEnabled = false;
@ -80,6 +94,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
isEdit = false; isEdit = false;
panelMode = true;
timewindow: Timewindow; timewindow: Timewindow;
timewindowForm: UntypedFormGroup; timewindowForm: UntypedFormGroup;
@ -125,7 +141,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
constructor(@Inject(TIMEWINDOW_PANEL_DATA) public data: TimewindowPanelData, constructor(@Inject(TIMEWINDOW_PANEL_DATA) public data: TimewindowPanelData,
public overlayRef: OverlayRef, @Optional() public overlayRef: OverlayRef,
protected store: Store<AppState>, protected store: Store<AppState>,
public fb: UntypedFormBuilder, public fb: UntypedFormBuilder,
private timeService: TimeService, private timeService: TimeService,
@ -140,6 +156,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
this.aggregation = data.aggregation; this.aggregation = data.aggregation;
this.timezone = data.timezone; this.timezone = data.timezone;
this.isEdit = data.isEdit; this.isEdit = data.isEdit;
this.panelMode = data.panelMode;
this.updateTimewindowAdvancedParams(); this.updateTimewindowAdvancedParams();
@ -356,6 +373,15 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
).subscribe((selectedTab: TimewindowType) => { ).subscribe((selectedTab: TimewindowType) => {
this.onTimewindowTypeChange(selectedTab); this.onTimewindowTypeChange(selectedTab);
}); });
if (!this.panelMode) {
this.timewindowForm.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.prepareTimewindowConfig();
this.changeTimewindow.emit(this.timewindow);
});
}
} }
ngOnDestroy() { ngOnDestroy() {
@ -381,7 +407,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
update() { update() {
this.prepareTimewindowConfig(); this.prepareTimewindowConfig();
this.result = this.timewindow; this.result = this.timewindow;
this.overlayRef.dispose(); this.overlayRef?.dispose();
} }
private prepareTimewindowConfig() { private prepareTimewindowConfig() {
@ -483,7 +509,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O
} }
cancel() { cancel() {
this.overlayRef.dispose(); this.overlayRef?.dispose();
} }
get minRealtimeAggInterval() { get minRealtimeAggInterval() {

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<button *ngIf="asButton && !strokedButton && !flatButton" <button *ngIf="asButton && panelMode && !strokedButton && !flatButton"
[disabled]="timewindowDisabled" [disabled]="timewindowDisabled"
type="button" type="button"
mat-raised-button color="primary" mat-raised-button color="primary"
@ -23,7 +23,7 @@
<mat-icon class="material-icons">query_builder</mat-icon> <mat-icon class="material-icons">query_builder</mat-icon>
<span>{{displayValue()}}</span> <span>{{displayValue()}}</span>
</button> </button>
<button *ngIf="asButton && strokedButton" <button *ngIf="asButton && panelMode && strokedButton"
[disabled]="timewindowDisabled" [disabled]="timewindowDisabled"
type="button" type="button"
mat-stroked-button color="primary" mat-stroked-button color="primary"
@ -31,7 +31,7 @@
<mat-icon class="material-icons">query_builder</mat-icon> <mat-icon class="material-icons">query_builder</mat-icon>
<span>{{displayValue()}}</span> <span>{{displayValue()}}</span>
</button> </button>
<button *ngIf="asButton && flatButton" <button *ngIf="asButton && panelMode && flatButton"
[disabled]="timewindowDisabled" [disabled]="timewindowDisabled"
type="button" type="button"
mat-button mat-button
@ -39,7 +39,7 @@
<mat-icon class="material-icons">query_builder</mat-icon> <mat-icon class="material-icons">query_builder</mat-icon>
<span>{{displayValue()}}</span> <span>{{displayValue()}}</span>
</button> </button>
<section *ngIf="!asButton" <section *ngIf="!asButton && panelMode"
class="tb-timewindow" class="tb-timewindow"
[class]="{'no-padding': noPadding}" [class]="{'no-padding': noPadding}"
matTooltip="{{ 'timewindow.edit' | translate }}" matTooltip="{{ 'timewindow.edit' | translate }}"
@ -55,3 +55,4 @@
<tb-icon *ngIf="computedTimewindowStyle.showIcon && computedTimewindowStyle.iconPosition === 'right'" <tb-icon *ngIf="computedTimewindowStyle.showIcon && computedTimewindowStyle.iconPosition === 'right'"
[style]="timewindowIconStyle">{{ computedTimewindowStyle.icon }}</tb-icon> [style]="timewindowIconStyle">{{ computedTimewindowStyle.icon }}</tb-icon>
</section> </section>
<ng-container #panelContainer></ng-container>

View File

@ -17,6 +17,7 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
ElementRef, ElementRef,
forwardRef, forwardRef,
HostBinding, HostBinding,
@ -26,6 +27,7 @@ import {
OnInit, OnInit,
SimpleChanges, SimpleChanges,
StaticProvider, StaticProvider,
ViewChild,
ViewContainerRef ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@ -63,6 +65,7 @@ import {
} from '@shared/models/widget-settings.models'; } from '@shared/models/widget-settings.models';
import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models';
import { fromEvent } from 'rxjs'; import { fromEvent } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// @dynamic // @dynamic
@Component({ @Component({
@ -79,6 +82,8 @@ import { fromEvent } from 'rxjs';
}) })
export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChanges { export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChanges {
@ViewChild('panelContainer', { read: ViewContainerRef, static: true }) panelContainer: ViewContainerRef;
historyOnlyValue = false; historyOnlyValue = false;
@Input() @Input()
@ -180,6 +185,10 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan
@coerceBoolean() @coerceBoolean()
disabled: boolean; disabled: boolean;
@Input()
@coerceBoolean()
panelMode = true;
innerValue: Timewindow; innerValue: Timewindow;
timewindowDisabled: boolean; timewindowDisabled: boolean;
@ -197,7 +206,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan
private datePipe: DatePipe, private datePipe: DatePipe,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private nativeElement: ElementRef, private nativeElement: ElementRef,
public viewContainerRef: ViewContainerRef) { private viewContainerRef: ViewContainerRef,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
@ -249,7 +259,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan
quickIntervalOnly: this.quickIntervalOnly, quickIntervalOnly: this.quickIntervalOnly,
aggregation: this.aggregation, aggregation: this.aggregation,
timezone: this.timezone, timezone: this.timezone,
isEdit: this.isEdit isEdit: this.isEdit,
panelMode: this.panelMode,
} as TimewindowPanelData } as TimewindowPanelData
}, },
{ {
@ -317,6 +328,9 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan
} else { } else {
this.updateDisplayValue(); this.updateDisplayValue();
} }
if (!this.panelMode) {
this.createPanel();
}
} }
notifyChanged() { notifyChanged() {
@ -328,6 +342,9 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan
} }
updateDisplayValue() { updateDisplayValue() {
if (!this.panelMode) {
return
}
if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) { if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) {
this.innerValue.displayValue = this.displayTypePrefix ? (this.translate.instant('timewindow.realtime') + ' - ') : ''; this.innerValue.displayValue = this.displayTypePrefix ? (this.translate.instant('timewindow.realtime') + ' - ') : '';
if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) { if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) {
@ -373,4 +390,29 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan
))); )));
} }
private createPanel() {
this.panelContainer.clear();
const panelData = {
timewindow: deepClone(this.innerValue),
historyOnly: this.historyOnly,
forAllTimeEnabled: this.forAllTimeEnabled,
quickIntervalOnly: this.quickIntervalOnly,
aggregation: this.aggregation,
timezone: this.timezone,
isEdit: this.isEdit,
panelMode: this.panelMode,
}
const injector = Injector.create({
providers: [{ provide: TIMEWINDOW_PANEL_DATA, useValue: panelData }],
parent: this.viewContainerRef.injector
});
const componentRef = this.panelContainer.createComponent(TimewindowPanelComponent, {index: 0, injector});
componentRef.instance.changeTimewindow.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(value => {
this.innerValue = value;
this.timewindowDisabled = this.isTimewindowDisabled();
this.notifyChanged();
})
}
} }

View File

@ -1406,3 +1406,28 @@ export const calculateInterval = (startTime: number, endTime: number,
export const getCurrentTimeForComparison = (timeForComparison: moment_.unitOfTime.DurationConstructor, tz?: string): moment_.Moment => export const getCurrentTimeForComparison = (timeForComparison: moment_.unitOfTime.DurationConstructor, tz?: string): moment_.Moment =>
getCurrentTime(tz).subtract(1, timeForComparison); getCurrentTime(tz).subtract(1, timeForComparison);
export const getTimePageLinkInterval = (timewindow: Timewindow): {startTime?: number; endTime?: number} => {
const interval: {startTime?: number; endTime?: number} = {};
switch (timewindow.history.historyType) {
case HistoryWindowType.LAST_INTERVAL:
const currentTime = Date.now();
interval.startTime = currentTime - timewindow.history.timewindowMs;
interval.endTime = currentTime;
break;
case HistoryWindowType.FIXED:
interval.startTime = timewindow.history.fixedTimewindow.startTimeMs;
interval.endTime = timewindow.history.fixedTimewindow.endTimeMs;
break;
case HistoryWindowType.INTERVAL:
const startEndTime = calculateIntervalStartEndTime(timewindow.history.quickInterval);
interval.startTime = startEndTime[0];
interval.endTime = startEndTime[1];
break;
case HistoryWindowType.FOR_ALL_TIME:
interval.startTime = null;
interval.endTime = null;
break;
}
return interval;
}