diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index f56f90de04..7b30759cdf 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Input, OnInit, ViewContainerRef } from '@angular/core'; +import { Component, Inject, InjectionToken, OnInit, ViewContainerRef } from '@angular/core'; import { aggregationTranslations, AggregationType, @@ -31,6 +31,7 @@ import { AppState } from '@core/core.state'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { TimeService } from '@core/services/time.service'; import { isDefined } from '@core/utils'; +import { OverlayRef } from '@angular/cdk/overlay'; export interface TimewindowPanelData { historyOnly: boolean; @@ -41,6 +42,8 @@ export interface TimewindowPanelData { isEdit: boolean; } +export const TIMEWINDOW_PANEL_DATA = new InjectionToken('TimewindowPanelData'); + @Component({ selector: 'tb-timewindow-panel', templateUrl: './timewindow-panel.component.html', @@ -48,12 +51,6 @@ export interface TimewindowPanelData { }) export class TimewindowPanelComponent extends PageComponent implements OnInit { - @Input() - data: any; - - @Input() - onClose: (result: Timewindow | null) => void; - historyOnly = false; quickIntervalOnly = false; @@ -80,23 +77,24 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { aggregationTypesTranslations = aggregationTranslations; - private result: Timewindow; + result: Timewindow; - constructor(protected store: Store, + constructor(@Inject(TIMEWINDOW_PANEL_DATA) public data: TimewindowPanelData, + public overlayRef: OverlayRef, + protected store: Store, public fb: UntypedFormBuilder, private timeService: TimeService, public viewContainerRef: ViewContainerRef) { super(store); + this.historyOnly = data.historyOnly; + this.quickIntervalOnly = data.quickIntervalOnly; + this.timewindow = data.timewindow; + this.aggregation = data.aggregation; + this.timezone = data.timezone; + this.isEdit = data.isEdit; } ngOnInit(): void { - this.historyOnly = this.data.historyOnly; - this.quickIntervalOnly = this.data.quickIntervalOnly; - this.timewindow = this.data.timewindow; - this.aggregation = this.data.aggregation; - this.timezone = this.data.timezone; - this.isEdit = this.data.isEdit; - const hideInterval = this.timewindow.hideInterval || false; const hideLastInterval = this.timewindow.hideLastInterval || false; const hideQuickInterval = this.timewindow.hideQuickInterval || false; @@ -111,46 +109,46 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { this.timewindowForm = this.fb.group({ realtime: this.fb.group({ realtimeType: [{ - value: this.defined(realtime, realtime.realtimeType) ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL, + value: isDefined(realtime?.realtimeType) ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL, disabled: hideInterval }], timewindowMs: [{ - value: this.defined(realtime, realtime.timewindowMs) ? this.timewindow.realtime.timewindowMs : null, + value: isDefined(realtime?.timewindowMs) ? this.timewindow.realtime.timewindowMs : null, disabled: hideInterval || hideLastInterval }], - interval: [this.defined(realtime, realtime.interval) ? this.timewindow.realtime.interval : null], + interval: [isDefined(realtime?.interval) ? this.timewindow.realtime.interval : null], quickInterval: [{ - value: this.defined(realtime, realtime.quickInterval) ? this.timewindow.realtime.quickInterval : null, + value: isDefined(realtime?.quickInterval) ? this.timewindow.realtime.quickInterval : null, disabled: hideInterval || hideQuickInterval }] }), history: this.fb.group({ historyType: [{ - value: this.defined(history, history.historyType) ? this.timewindow.history.historyType : HistoryWindowType.LAST_INTERVAL, + value: isDefined(history?.historyType) ? this.timewindow.history.historyType : HistoryWindowType.LAST_INTERVAL, disabled: hideInterval }], timewindowMs: [{ - value: this.defined(history, history.timewindowMs) ? this.timewindow.history.timewindowMs : null, + value: isDefined(history?.timewindowMs) ? this.timewindow.history.timewindowMs : null, disabled: hideInterval }], - interval: [ this.defined(history, history.interval) ? this.timewindow.history.interval : null + interval: [ isDefined(history?.interval) ? this.timewindow.history.interval : null ], fixedTimewindow: [{ - value: this.defined(history, history.fixedTimewindow) ? this.timewindow.history.fixedTimewindow : null, + value: isDefined(history?.fixedTimewindow) ? this.timewindow.history.fixedTimewindow : null, disabled: hideInterval }], quickInterval: [{ - value: this.defined(history, history.quickInterval) ? this.timewindow.history.quickInterval : null, + value: isDefined(history?.quickInterval) ? this.timewindow.history.quickInterval : null, disabled: hideInterval }] }), aggregation: this.fb.group({ type: [{ - value: this.defined(aggregation, aggregation.type) ? this.timewindow.aggregation.type : null, + value: isDefined(aggregation?.type) ? this.timewindow.aggregation.type : null, disabled: hideAggregation }], limit: [{ - value: this.defined(aggregation, aggregation.limit) ? this.checkLimit(this.timewindow.aggregation.limit) : null, + value: isDefined(aggregation?.limit) ? this.checkLimit(this.timewindow.aggregation.limit) : null, disabled: hideAggInterval }, []] }), @@ -165,10 +163,6 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { }); } - private defined(arg1, arg2) { - return arg1 && isDefined(arg2); - } - private checkLimit(limit?: number): number { if (!limit || limit < this.minDatapointsLimit()) { return this.minDatapointsLimit(); @@ -214,13 +208,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { this.timewindow.timezone = timewindowFormValue.timezone; } this.result = this.timewindow; - this.cancel(this.result); + this.overlayRef.dispose(); } - cancel(result: Timewindow | null = null) { - if (this.onClose) { - this.onClose(result); - } + cancel() { + this.overlayRef.dispose(); } minDatapointsLimit() { diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts index 0674e13a88..0c87020d5b 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -18,10 +18,11 @@ import { ChangeDetectorRef, Component, forwardRef, + Injector, Input, OnDestroy, OnInit, - Renderer2, + StaticProvider, ViewContainerRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @@ -38,14 +39,15 @@ import { TimewindowType } from '@shared/models/time/time.models'; import { DatePipe } from '@angular/common'; -import { TimewindowPanelComponent } from '@shared/components/time/timewindow-panel.component'; +import { TIMEWINDOW_PANEL_DATA, TimewindowPanelComponent } from '@shared/components/time/timewindow-panel.component'; import { MediaBreakpoints } from '@shared/models/constants'; import { BreakpointObserver } from '@angular/cdk/layout'; import { TimeService } from '@core/services/time.service'; import { TooltipPosition } from '@angular/material/tooltip'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { TbPopoverService } from '@shared/components/popover.service'; +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; // @dynamic @Component({ @@ -171,13 +173,12 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces private propagateChange = (_: any) => {}; - constructor(private translate: TranslateService, + constructor(private overlay: Overlay, + private translate: TranslateService, private timeService: TimeService, private millisecondsToTimeStringPipe: MillisecondsToTimeStringPipe, private datePipe: DatePipe, private cd: ChangeDetectorRef, - private popoverService: TbPopoverService, - private renderer: Renderer2, public viewContainerRef: ViewContainerRef, public breakpointObserver: BreakpointObserver) { } @@ -192,35 +193,54 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces if ($event) { $event.stopPropagation(); } - const trigger = ($event.target || $event.srcElement || $event.currentTarget) as Element; - if (this.popoverService.hasPopover(trigger)) { - this.popoverService.hidePopover(trigger); - } else { - const timewindowPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, TimewindowPanelComponent, 'leftTop', true, null, - { - data: { - timewindow: deepClone(this.innerValue), - historyOnly: this.historyOnly, - quickIntervalOnly: this.quickIntervalOnly, - aggregation: this.aggregation, - timezone: this.timezone, - isEdit: this.isEdit - }, - onClose: (result: Timewindow) => { - timewindowPopover.hide(); - if (result) { - this.innerValue = result; - this.timewindowDisabled = this.isTimewindowDisabled(); - this.updateDisplayValue(); - this.notifyChanged(); - } - } - }, - {maxHeight: '100vh', height: '100%', padding: '10px'}, - {minWidth: '100%', maxWidth: '100%'}, {}, false); - timewindowPopover.tbComponentRef.instance.popoverComponent = timewindowPopover; - } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + config.maxHeight = '70vh'; + config.height = 'min-content'; + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const providers: StaticProvider[] = [ + { + provide: TIMEWINDOW_PANEL_DATA, + useValue: { + timewindow: deepClone(this.innerValue), + historyOnly: this.historyOnly, + quickIntervalOnly: this.quickIntervalOnly, + aggregation: this.aggregation, + timezone: this.timezone, + isEdit: this.isEdit + } + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(TimewindowPanelComponent, + this.viewContainerRef, injector)); + componentRef.onDestroy(() => { + if (componentRef.instance.result) { + this.innerValue = componentRef.instance.result; + this.timewindowDisabled = this.isTimewindowDisabled(); + this.updateDisplayValue(); + this.notifyChanged(); + } + }); + this.cd.detectChanges(); } private onHistoryOnlyChanged(): boolean {