Browse Source

UI: display offset in timezone component

pull/11633/head
Chantsova Ekaterina 2 years ago
parent
commit
7720096837
  1. 25
      ui-ngx/src/app/core/services/time.service.ts
  2. 17
      ui-ngx/src/app/shared/components/time/timewindow-panel.component.html
  3. 5
      ui-ngx/src/app/shared/components/time/timezone-panel.component.html
  4. 3
      ui-ngx/src/app/shared/components/time/timezone-panel.component.scss
  5. 59
      ui-ngx/src/app/shared/components/time/timezone-panel.component.ts
  6. 2
      ui-ngx/src/app/shared/components/time/timezone-select.component.html
  7. 27
      ui-ngx/src/app/shared/components/time/timezone-select.component.ts
  8. 14
      ui-ngx/src/app/shared/components/time/timezone.component.html
  9. 4
      ui-ngx/src/app/shared/components/time/timezone.component.scss
  10. 167
      ui-ngx/src/app/shared/components/time/timezone.component.ts
  11. 2
      ui-ngx/src/styles.scss

25
ui-ngx/src/app/core/services/time.service.ts

@ -19,13 +19,18 @@ import {
AggregationType,
DAY,
defaultTimeIntervals,
defaultTimewindow, Interval, IntervalMath,
defaultTimewindow,
getDefaultTimezoneInfo,
Interval,
IntervalMath,
SECOND,
TimeInterval,
Timewindow
Timewindow,
TimezoneInfo
} from '@shared/models/time/time.models';
import { HttpClient } from '@angular/common/http';
import { isDefined } from '@core/utils';
import { deepClone, isDefined } from '@core/utils';
import { TranslateService } from '@ngx-translate/core';
const MIN_INTERVAL = SECOND;
const MAX_INTERVAL = 365 * 20 * DAY;
@ -41,8 +46,11 @@ export class TimeService {
private maxDatapointsLimit = MAX_DATAPOINTS_LIMIT;
private localBrowserTimezoneInfoPlaceholder: TimezoneInfo;
constructor(
private http: HttpClient
private http: HttpClient,
private translate: TranslateService
) {}
public setMaxDatapointsLimit(limit: number) {
@ -161,4 +169,13 @@ export class TimeService {
return defValue;
}
}
public getLocalBrowserTimezoneInfoPlaceholder(): TimezoneInfo {
if (!this.localBrowserTimezoneInfoPlaceholder) {
this.localBrowserTimezoneInfoPlaceholder = deepClone(getDefaultTimezoneInfo());
this.localBrowserTimezoneInfoPlaceholder.id = null;
this.localBrowserTimezoneInfoPlaceholder.name = this.translate.instant('timezone.browser-time');
}
return this.localBrowserTimezoneInfoPlaceholder;
}
}

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

@ -59,10 +59,12 @@
</ng-container>
</ng-container>
</section>
<ng-container *ngIf="!isEdit && (timewindow.hideInterval || (timewindow.hideLastInterval && timewindow.hideQuickInterval))">
<section class="tb-form-panel stroked tb-flex row align-center space-between"
*ngIf="!isEdit && (timewindow.hideInterval || (timewindow.hideLastInterval && timewindow.hideQuickInterval))">
<div class="tb-form-panel-title">{{ 'timezone.timezone' | translate }}</div>
<ng-container *ngTemplateOutlet="timezoneSelection">
</ng-container>
</ng-container>
</section>
<ng-container *ngTemplateOutlet="aggregationConfig">
</ng-container>
</div>
@ -115,10 +117,12 @@
</ng-container>
</ng-container>
</section>
<ng-container *ngIf="!isEdit && timewindow.hideInterval">
<section class="tb-form-panel stroked tb-flex row align-center space-between"
*ngIf="!isEdit && timewindow.hideInterval">
<div class="tb-form-panel-title">{{ 'timezone.timezone' | translate }}</div>
<ng-container *ngTemplateOutlet="timezoneSelection">
</ng-container>
</ng-container>
</section>
<ng-container *ngTemplateOutlet="aggregationConfig">
</ng-container>
</div>
@ -189,7 +193,10 @@
</ng-template>
<ng-template #timezoneSelection>
<ng-container *ngIf="timezone && (isEdit || !timewindow.hideTimezone)">
<tb-timezone asButton strokedButton displayTimezoneValue="false" formControlName="timezone"></tb-timezone>
<tb-timezone asButton strokedButton noMargin
localBrowserTimezonePlaceholderOnEmpty="true"
formControlName="timezone">
</tb-timezone>
</ng-container>
</ng-template>
</form>

5
ui-ngx/src/app/shared/components/time/timezone-panel.component.html

@ -1,10 +1,11 @@
<form [formGroup]="timezoneForm" class="mat-content">
<section class="tb-form-panel">
<div class="tb-form-panel-title">{{ 'timezone.timezone' | translate }}</div>
<tb-timezone-select localBrowserTimezonePlaceholderOnEmpty="true"
<tb-timezone-select [localBrowserTimezonePlaceholderOnEmpty]="localBrowserTimezonePlaceholderOnEmpty"
formControlName="timezone"
subscriptSizing="dynamic"
appearance="outline">
appearance="outline"
displayLabel="false">
</tb-timezone-select>
</section>
</form>

3
ui-ngx/src/app/shared/components/time/timezone-panel.component.scss

@ -18,13 +18,14 @@
:host {
display: flex;
flex-direction: column;
width: 380px;
max-height: 100%;
max-width: 100%;
background-color: #fff;
.tb-form-panel {
padding: 12px;
gap: 12px;
gap: 24px;
}
.tb-flex {

59
ui-ngx/src/app/shared/components/time/timezone-panel.component.ts

@ -14,22 +14,17 @@
/// limitations under the License.
///
import { Component, Inject, InjectionToken, OnInit, ViewContainerRef } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { OverlayRef } from '@angular/cdk/overlay';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup } from '@angular/forms';
import { TimeService } from '@core/services/time.service';
import { TranslateService } from '@ngx-translate/core';
import { TbPopoverComponent } from '@shared/components/popover.component';
export interface TimezonePanelData {
timezone: string;
isEdit: boolean;
export interface TimezoneSelectionResult {
timezone: string | null;
}
export const TIMEZONE_PANEL_DATA = new InjectionToken<any>('TimezonePanelData');
@Component({
selector: 'tb-timezone-panel',
templateUrl: './timezone-panel.component.html',
@ -37,35 +32,49 @@ export const TIMEZONE_PANEL_DATA = new InjectionToken<any>('TimezonePanelData');
})
export class TimezonePanelComponent extends PageComponent implements OnInit {
timezone: string;
result: string;
@Input()
timezone: string | null;
@Input()
userTimezoneByDefault: boolean;
@Input()
localBrowserTimezonePlaceholderOnEmpty: boolean;
@Input()
defaultTimezone: string;
@Input()
onClose: (result: TimezoneSelectionResult | null) => void;
@Input()
popoverComponent: TbPopoverComponent;
timezoneForm: FormGroup;
constructor(@Inject(TIMEZONE_PANEL_DATA) public data: TimezonePanelData,
public overlayRef: OverlayRef,
protected store: Store<AppState>,
public fb: FormBuilder,
private timeService: TimeService,
private translate: TranslateService,
public viewContainerRef: ViewContainerRef) {
constructor(protected store: Store<AppState>,
public fb: FormBuilder) {
super(store);
this.timezone = this.data.timezone;
this.timezoneForm = this.fb.group({
timezone: [this.timezone]
});
}
ngOnInit(): void {
this.timezoneForm = this.fb.group({
timezone: [this.timezone]
});
}
update() {
this.result = this.timezoneForm.get('timezone').value;
this.overlayRef.dispose();
if (this.onClose) {
this.onClose({
timezone: this.timezoneForm.get('timezone').value
});
}
}
cancel() {
this.overlayRef.dispose();
if (this.onClose) {
this.onClose(null);
}
}
}

2
ui-ngx/src/app/shared/components/time/timezone-select.component.html

@ -17,7 +17,7 @@
-->
<mat-form-field [formGroup]="selectTimezoneFormGroup" fxFlex class="mat-block"
[subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timezone.timezone</mat-label>
<mat-label *ngIf="displayLabel" translate>timezone.timezone</mat-label>
<input matInput type="text" placeholder="{{ 'timezone.select-timezone' | translate }}"
#timezoneInput
formControlName="timezone"

27
ui-ngx/src/app/shared/components/time/timezone-select.component.ts

@ -15,7 +15,7 @@
///
import { AfterViewInit, Component, forwardRef, Input, NgZone, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
import { Observable, of } from 'rxjs';
import { map, mergeMap, share, tap } from 'rxjs/operators';
@ -26,6 +26,8 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { getDefaultTimezoneInfo, getTimezoneInfo, getTimezones, TimezoneInfo } from '@shared/models/time/time.models';
import { deepClone } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion';
import { TimeService } from '@core/services/time.service';
@Component({
selector: 'tb-timezone-select',
@ -52,6 +54,10 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af
}
}
@Input()
@coerceBoolean()
displayLabel = true;
@Input()
subscriptSizing: SubscriptSizing = 'fixed';
@ -98,7 +104,7 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af
private dirty = false;
private localBrowserTimezoneInfoPlaceholder: TimezoneInfo;
private localBrowserTimezoneInfoPlaceholder: TimezoneInfo = this.timeService.getLocalBrowserTimezoneInfoPlaceholder();
private timezones: Array<TimezoneInfo>;
@ -107,7 +113,8 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af
constructor(private store: Store<AppState>,
public translate: TranslateService,
private ngZone: NgZone,
private fb: UntypedFormBuilder) {
private fb: UntypedFormBuilder,
private timeService: TimeService) {
this.selectTimezoneFormGroup = this.fb.group({
timezone: [null]
});
@ -168,7 +175,7 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af
} else {
this.modelValue = null;
if (this.localBrowserTimezonePlaceholderOnEmptyValue) {
this.selectTimezoneFormGroup.get('timezone').patchValue(this.getLocalBrowserTimezoneInfoPlaceholder(), {emitEvent: false});
this.selectTimezoneFormGroup.get('timezone').patchValue(this.localBrowserTimezoneInfoPlaceholder, {emitEvent: false});
} else {
this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false});
}
@ -197,7 +204,7 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af
}
} else if (this.localBrowserTimezonePlaceholderOnEmptyValue) {
this.ngZone.run(() => {
this.selectTimezoneFormGroup.get('timezone').reset(this.getLocalBrowserTimezoneInfoPlaceholder(), {emitEvent: true});
this.selectTimezoneFormGroup.get('timezone').reset(this.localBrowserTimezoneInfoPlaceholder, {emitEvent: true});
});
}
}
@ -235,19 +242,11 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af
if (!this.timezones) {
this.timezones = [];
if (this.localBrowserTimezonePlaceholderOnEmptyValue) {
this.timezones.push(this.getLocalBrowserTimezoneInfoPlaceholder());
this.timezones.push(this.localBrowserTimezoneInfoPlaceholder);
}
this.timezones.push(...getTimezones());
}
return this.timezones;
}
private getLocalBrowserTimezoneInfoPlaceholder(): TimezoneInfo {
if (!this.localBrowserTimezoneInfoPlaceholder) {
this.localBrowserTimezoneInfoPlaceholder = deepClone(getDefaultTimezoneInfo());
this.localBrowserTimezoneInfoPlaceholder.id = null;
this.localBrowserTimezoneInfoPlaceholder.name = this.translate.instant('timezone.browser-time');
}
return this.localBrowserTimezoneInfoPlaceholder;
}
}

14
ui-ngx/src/app/shared/components/time/timezone.component.html

@ -17,6 +17,8 @@
-->
<button *ngIf="asButton && !strokedButton && !flatButton"
[disabled]="timezoneDisabled"
[matTooltip]="tooltipValue()"
[matTooltipPosition]="tooltipPosition"
type="button"
mat-raised-button color="primary"
(click)="toggleTimezone($event)">
@ -25,6 +27,8 @@
</button>
<button *ngIf="asButton && strokedButton"
[disabled]="timezoneDisabled"
[matTooltip]="tooltipValue()"
[matTooltipPosition]="tooltipPosition"
type="button"
mat-stroked-button color="primary"
(click)="toggleTimezone($event)">
@ -33,6 +37,8 @@
</button>
<button *ngIf="asButton && flatButton"
[disabled]="timezoneDisabled"
[matTooltip]="tooltipValue()"
[matTooltipPosition]="tooltipPosition"
type="button"
mat-button
(click)="toggleTimezone($event)">
@ -40,16 +46,16 @@
<span>{{displayValue()}}</span>
</button>
<section *ngIf="!asButton"
class="tb-timewindow"
class="tb-timezone"
[class]="{'no-padding': noPadding}"
matTooltip="{{ 'timewindow.edit' | translate }}"
[matTooltip]="tooltipValue()"
[matTooltipPosition]="tooltipPosition"
[style]="timezoneComponentStyle"
(click)="toggleTimezone($event)">
<tb-icon *ngIf="computedTimezoneStyle.showIcon && computedTimezoneStyle.iconPosition === 'left'"
[style]="timezoneIconStyle">{{ computedTimezoneStyle.icon }}</tb-icon>
<div class="tb-timewindow-label" [fxHide]="hideLabel">
{{innerValue}}
<div class="tb-timezone-label" [fxHide]="hideLabel">
{{timezoneInfo}}
</div>
<tb-icon *ngIf="computedTimezoneStyle.showIcon && computedTimezoneStyle.iconPosition === 'right'"
[style]="timezoneIconStyle">{{ computedTimezoneStyle.icon }}</tb-icon>

4
ui-ngx/src/app/shared/components/time/timezone.component.scss

@ -23,7 +23,7 @@
.mdc-button {
max-width: 100%;
}
section.tb-timewindow {
section.tb-timezone {
padding: 0 8px;
&.no-padding {
padding: 0;
@ -38,7 +38,7 @@
gap: 4px;
width: 100%;
.tb-timewindow-label {
.tb-timezone-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

167
ui-ngx/src/app/shared/components/time/timezone.component.ts

@ -17,26 +17,19 @@
import {
ChangeDetectorRef,
Component,
ElementRef,
forwardRef,
HostBinding,
Injector,
Input,
OnChanges,
OnInit, Renderer2,
OnInit,
Renderer2,
SimpleChanges,
StaticProvider,
ViewContainerRef
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
import { DatePipe } from '@angular/common';
import { TimeService } from '@core/services/time.service';
import { TooltipPosition } from '@angular/material/tooltip';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { coerceBoolean } from '@shared/decorators/coercion';
import {
ComponentStyle,
@ -45,15 +38,10 @@ import {
textStyle,
TimezoneStyle
} from '@shared/models/widget-settings.models';
import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models';
import { fromEvent } from 'rxjs';
import {
TIMEZONE_PANEL_DATA,
TimezonePanelComponent,
TimezonePanelData
} from '@shared/components/time/timezone-panel.component';
import { TimezonePanelComponent, TimezoneSelectionResult } from '@shared/components/time/timezone-panel.component';
import { TbPopoverService } from '@shared/components/popover.service';
import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component';
import { getTimezoneInfo, TimezoneInfo } from '@shared/models/time/time.models';
import { TimeService } from '@core/services/time.service';
// @dynamic
@Component({
@ -83,10 +71,6 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
@coerceBoolean()
disablePanel = false;
@Input()
@coerceBoolean()
isToolbar = false;
@Input()
@coerceBoolean()
asButton = false;
@ -145,7 +129,8 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
@Input()
set localBrowserTimezonePlaceholderOnEmpty(value: boolean) {
this.localBrowserTimezonePlaceholderOnEmptyValue = coerceBooleanProperty(value);
}defaultTimezoneId: string = null;
}
defaultTimezoneId: string = null;
@Input()
set defaultTimezone(timezone: string) {
@ -163,7 +148,10 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
this.requiredValue = coerceBooleanProperty(value);
}
innerValue: string;
modelValue: string | null;
timezoneInfo: TimezoneInfo;
private localBrowserTimezoneInfoPlaceholder: TimezoneInfo = this.timeService.getLocalBrowserTimezoneInfoPlaceholder();
timezoneDisabled: boolean;
@ -173,16 +161,12 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
private propagateChange = (_: any) => {};
constructor(private overlay: Overlay,
private translate: TranslateService,
private timeService: TimeService,
private millisecondsToTimeStringPipe: MillisecondsToTimeStringPipe,
private datePipe: DatePipe,
constructor(private translate: TranslateService,
private cd: ChangeDetectorRef,
private nativeElement: ElementRef,
public viewContainerRef: ViewContainerRef,
private popoverService: TbPopoverService,
private renderer: Renderer2) {
private renderer: Renderer2,
private timeService: TimeService) {
}
ngOnInit() {
@ -202,82 +186,38 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
}
toggleTimezone($event: Event) {
console.log($event);
if ($event) {
$event.stopPropagation();
}
if (this.disablePanel) {
return;
}
// const trigger = ($event.target || $event.srcElement || $event.currentTarget) as Element;
// if (this.popoverService.hasPopover(trigger)) {
// this.popoverService.hidePopover(trigger);
// } else {
// const timezoneSelectionPopover = this.popoverService.displayPopover(trigger, this.renderer,
// this.viewContainerRef, TimewindowStylePanelComponent, 'left', true, null,
// ctx,
// {},
// {}, {}, true);
// timewindowStylePanelPopover.tbComponentRef.instance.popover = timewindowStylePanelPopover;
// timewindowStylePanelPopover.tbComponentRef.instance.timewindowStyleApplied.subscribe((timewindowStyle) => {
// timewindowStylePanelPopover.hide();
// this.modelValue = timewindowStyle;
// this.propagateChange(this.modelValue);
// });
//
// // if (componentRef.instance.result) {
// // this.innerValue = componentRef.instance.result;
// // this.timezoneDisabled = this.isTimezoneDisabled();
// // this.updateDisplayValue();
// // this.notifyChanged();
// // }
// }
const config = new OverlayConfig({
panelClass: 'tb-timezone-panel',
backdropClass: 'cdk-overlay-transparent-backdrop',
hasBackdrop: true,
maxHeight: '30vh',
height: 'min-content'
});
config.positionStrategy = this.overlay.position()
.flexibleConnectedTo(this.nativeElement)
.withPositions(DEFAULT_OVERLAY_POSITIONS);
// TODO: change panel to popover
const overlayRef = this.overlay.create(config);
overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose();
});
const providers: StaticProvider[] = [
{
provide: TIMEZONE_PANEL_DATA,
useValue: {
timezone: this.innerValue,
isEdit: this.isEdit
} as TimezonePanelData
},
{
provide: OverlayRef,
useValue: overlayRef
}
];
const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
const componentRef = overlayRef.attach(new ComponentPortal(TimezonePanelComponent,
this.viewContainerRef, injector));
const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => {
overlayRef.updatePosition();
});
componentRef.onDestroy(() => {
resizeWindows$.unsubscribe();
// TODO: if value check makes impossible to select browser timezone (null)
if (componentRef.instance.result) {
this.innerValue = componentRef.instance.result;
this.timezoneDisabled = this.isTimezoneDisabled();
this.updateDisplayValue();
this.notifyChanged();
}
});
const trigger = ($event.target || $event.srcElement || $event.currentTarget) as Element;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const timezoneSelectionPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, TimezonePanelComponent, ['bottomRight', 'leftBottom'], true, null,
{
timezone: this.modelValue,
userTimezoneByDefault: this.userTimezoneByDefaultValue,
localBrowserTimezonePlaceholderOnEmpty: this.localBrowserTimezonePlaceholderOnEmptyValue,
defaultTimezone: this.defaultTimezoneId,
onClose: (result: TimezoneSelectionResult | null) => {
timezoneSelectionPopover.hide();
if (result) {
this.modelValue = result.timezone;
this.setTimezoneInfo();
this.timezoneDisabled = this.isTimezoneDisabled();
this.updateDisplayValue();
this.notifyChanged();
}
}
},
{},
{}, {}, true);
timezoneSelectionPopover.tbComponentRef.instance.popoverComponent = timezoneSelectionPopover;
}
this.cd.detectChanges();
}
@ -304,19 +244,23 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
this.timezoneDisabled = this.isTimezoneDisabled();
}
writeValue(value: string): void {
this.innerValue = value;
writeValue(value: string | null): void {
this.modelValue = value;
this.setTimezoneInfo();
this.timezoneDisabled = this.isTimezoneDisabled();
this.updateDisplayValue();
}
notifyChanged() {
this.propagateChange(this.innerValue);
this.propagateChange(this.modelValue);
}
displayValue(): string {
// TODO: only offset should be displayed, timezone name shouldn't be present
return this.displayTimezoneValue ? this.innerValue : this.translate.instant('timezone.timezone');
return this.displayTimezoneValue && this.timezoneInfo ? this.timezoneInfo.offset : this.translate.instant('timezone.timezone');
}
tooltipValue(): string {
return this.timezoneInfo ? `${this.timezoneInfo.name} (${this.timezoneInfo.offset})` : undefined;
}
updateDisplayValue() {
@ -327,4 +271,17 @@ export class TimezoneComponent implements ControlValueAccessor, OnInit, OnChange
return this.disabled;
}
private setTimezoneInfo() {
const foundTimezone = getTimezoneInfo(this.modelValue, this.defaultTimezoneId, this.userTimezoneByDefaultValue);
if (foundTimezone !== null) {
this.timezoneInfo = foundTimezone;
} else {
if (this.localBrowserTimezonePlaceholderOnEmptyValue) {
this.timezoneInfo = this.localBrowserTimezoneInfoPlaceholder;
} else {
this.timezoneInfo = null;
}
}
}
}

2
ui-ngx/src/styles.scss

@ -268,7 +268,7 @@ pre.tb-highlight {
letter-spacing: normal;
}
.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel, .tb-panel-container, tb-timezone-panel {
.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel, .tb-panel-container {
overflow: hidden;
background: #fff;
border-radius: 4px;

Loading…
Cancel
Save