diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts
index c67a59ac20..6c25585f76 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts
@@ -23,7 +23,10 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { isUndefined } from '@core/utils';
-import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
+import {
+ getTimewindowConfig,
+ setTimewindowConfig
+} from '@home/components/widget/config/timewindow-config-panel.component';
@Component({
selector: 'tb-alarms-table-basic-config',
@@ -74,9 +77,7 @@ export class AlarmsTableBasicConfigComponent extends BasicWidgetConfigComponent
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
- this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
- this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
- this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
+ setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.alarmFilterConfig = config.alarmFilterConfig;
this.widgetConfig.config.alarmSource = config.datasources[0];
this.setColumns(config.columns, this.widgetConfig.config.alarmSource);
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html
index b4c2dbd616..ae0ca13dbe 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html
@@ -64,6 +64,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts
index f407293064..228879e2ef 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts
@@ -29,7 +29,10 @@ import {
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { isUndefined } from '@core/utils';
-import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
+import {
+ getTimewindowConfig,
+ setTimewindowConfig
+} from '@home/components/widget/config/timewindow-config-panel.component';
@Component({
selector: 'tb-entities-table-basic-config',
@@ -93,9 +96,7 @@ export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponen
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
- this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
- this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
- this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
+ setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
this.setColumns(config.columns, this.widgetConfig.config.datasources);
this.widgetConfig.config.actions = config.actions;
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts
index 283e39362a..9b2a6a764b 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts
@@ -27,7 +27,10 @@ import {
} from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
-import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
+import {
+ getTimewindowConfig,
+ setTimewindowConfig
+} from '@home/components/widget/config/timewindow-config-panel.component';
import { isUndefined } from '@core/utils';
import { getLabel, setLabel } from '@shared/models/widget-settings.models';
@@ -80,9 +83,7 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
- this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
- this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
- this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
+ setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
setLabel(config.label, this.widgetConfig.config.datasources);
this.widgetConfig.config.actions = config.actions;
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html
index 5952c206b6..15c903736a 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html
@@ -64,6 +64,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts
index a8c4206fa7..dc2a41b816 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts
@@ -24,7 +24,10 @@ import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { deepClone, isUndefined } from '@core/utils';
-import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
+import {
+ getTimewindowConfig,
+ setTimewindowConfig
+} from '@home/components/widget/config/timewindow-config-panel.component';
@Component({
selector: 'tb-timeseries-table-basic-config',
@@ -79,9 +82,7 @@ export class TimeseriesTableBasicConfigComponent extends BasicWidgetConfigCompon
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
- this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
- this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
- this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
+ setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
this.setColumns(config.columns, this.widgetConfig.config.datasources);
this.widgetConfig.config.actions = config.actions;
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html
index 356612564f..a036ab2b1c 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html
@@ -65,6 +65,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts
index b535aaa7f8..dacb2dfa11 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts
@@ -27,7 +27,10 @@ import {
} from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
-import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
+import {
+ getTimewindowConfig,
+ setTimewindowConfig
+} from '@home/components/widget/config/timewindow-config-panel.component';
import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils';
import {
DateFormatProcessor,
@@ -145,9 +148,7 @@ export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent {
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
- this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
- this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
- this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
+ setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html
index 0d607c230d..6bd31f41eb 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html
@@ -64,6 +64,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts
index c77d0ecb76..050d362868 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts
@@ -24,7 +24,10 @@ import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { isUndefined } from '@core/utils';
-import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
+import {
+ getTimewindowConfig,
+ setTimewindowConfig
+} from '@home/components/widget/config/timewindow-config-panel.component';
@Component({
selector: 'tb-flot-basic-config',
@@ -83,9 +86,7 @@ export class FlotBasicConfigComponent extends BasicWidgetConfigComponent {
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
- this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
- this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
- this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
+ setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
this.setSeries(config.series, this.widgetConfig.config.datasources);
this.widgetConfig.config.actions = config.actions;
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.html
index 045d41ee03..d144cb3db6 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.html
@@ -27,14 +27,19 @@
{{ 'widget-config.display-timewindow' | translate }}
-
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts
index 410896dd74..93fc304b7a 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts
@@ -22,11 +22,13 @@ import { Timewindow } from '@shared/models/time/time.models';
import { TranslateService } from '@ngx-translate/core';
import { coerceBoolean } from '@shared/decorators/coercion';
import { isDefined } from '@core/utils';
+import { TimewindowStyle } from '@shared/models/widget-settings.models';
export interface TimewindowConfigData {
useDashboardTimewindow: boolean;
displayTimewindow: boolean;
timewindow: Timewindow;
+ timewindowStyle: TimewindowStyle;
}
export const getTimewindowConfig = (config: WidgetConfig): TimewindowConfigData => ({
@@ -34,9 +36,17 @@ export const getTimewindowConfig = (config: WidgetConfig): TimewindowConfigData
config.useDashboardTimewindow : true,
displayTimewindow: isDefined(config.displayTimewindow) ?
config.displayTimewindow : true,
- timewindow: config.timewindow
+ timewindow: config.timewindow,
+ timewindowStyle: config.timewindowStyle
});
+export const setTimewindowConfig = (config: WidgetConfig, data: TimewindowConfigData): void => {
+ config.useDashboardTimewindow = data.useDashboardTimewindow;
+ config.displayTimewindow = data.displayTimewindow;
+ config.timewindow = data.timewindow;
+ config.timewindowStyle = data.timewindowStyle;
+};
+
@Component({
selector: 'tb-timewindow-config-panel',
templateUrl: './timewindow-config-panel.component.html',
@@ -78,7 +88,8 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI
this.timewindowConfig = this.fb.group({
useDashboardTimewindow: [null, []],
displayTimewindow: [null, []],
- timewindow: [null, []]
+ timewindow: [null, []],
+ timewindowStyle: [null, []]
});
this.timewindowConfig.valueChanges.subscribe(
(val) => this.propagateChange(val)
@@ -86,6 +97,9 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI
this.timewindowConfig.get('useDashboardTimewindow').valueChanges.subscribe(() => {
this.updateTimewindowConfigEnabledState();
});
+ this.timewindowConfig.get('displayTimewindow').valueChanges.subscribe(() => {
+ this.updateTimewindowConfigEnabledState();
+ });
}
writeValue(data?: TimewindowConfigData): void {
@@ -112,12 +126,19 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI
private updateTimewindowConfigEnabledState() {
const useDashboardTimewindow: boolean = this.timewindowConfig.get('useDashboardTimewindow').value;
+ const displayTimewindow: boolean = this.timewindowConfig.get('displayTimewindow').value;
if (useDashboardTimewindow) {
this.timewindowConfig.get('displayTimewindow').disable({emitEvent: false});
this.timewindowConfig.get('timewindow').disable({emitEvent: false});
+ this.timewindowConfig.get('timewindowStyle').disable({emitEvent: false});
} else {
this.timewindowConfig.get('displayTimewindow').enable({emitEvent: false});
this.timewindowConfig.get('timewindow').enable({emitEvent: false});
+ if (displayTimewindow) {
+ this.timewindowConfig.get('timewindowStyle').enable({emitEvent: false});
+ } else {
+ this.timewindowConfig.get('timewindowStyle').disable({emitEvent: false});
+ }
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.html
new file mode 100644
index 0000000000..5ba8df3012
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.html
@@ -0,0 +1,97 @@
+
+
+
timewindow.style
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.scss
new file mode 100644
index 0000000000..d3c2dfb4fb
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.scss
@@ -0,0 +1,54 @@
+/**
+ * Copyright © 2016-2023 The Thingsboard Authors
+ *
+ * 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.
+ */
+
+@import '../../../../../../scss/constants';
+
+.tb-timewindow-style-panel {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ @media #{$mat-gt-xs} {
+ min-width: 320px;
+ }
+ .tb-timewindow-style-title {
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 24px;
+ letter-spacing: 0.25px;
+ color: rgba(0, 0, 0, 0.87);
+ }
+ .tb-form-row {
+ .fixed-title-width {
+ min-width: 120px;
+ }
+ &.timewindow-preview {
+ align-items: flex-start;
+ tb-timewindow {
+ font-size: 14px;
+ opacity: .85;
+ }
+ }
+ }
+ .tb-timewindow-style-panel-buttons {
+ height: 60px;
+ display: flex;
+ flex-direction: row;
+ gap: 16px;
+ justify-content: flex-end;
+ align-items: flex-end;
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts
new file mode 100644
index 0000000000..e0e4ee5615
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts
@@ -0,0 +1,108 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// 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.
+///
+
+import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
+import { PageComponent } from '@shared/components/page.component';
+import { defaultTimewindowStyle, TimewindowStyle } from '@shared/models/widget-settings.models';
+import { TbPopoverComponent } from '@shared/components/popover.component';
+import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { Timewindow } from '@shared/models/time/time.models';
+import { deepClone } from '@core/utils';
+
+@Component({
+ selector: 'tb-timewindow-style-panel',
+ templateUrl: './timewindow-style-panel.component.html',
+ providers: [],
+ styleUrls: ['./timewindow-style-panel.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class TimewindowStylePanelComponent extends PageComponent implements OnInit {
+
+ @Input()
+ timewindowStyle: TimewindowStyle;
+
+ @Input()
+ previewValue: Timewindow;
+
+ @Input()
+ popover: TbPopoverComponent
;
+
+ @Output()
+ timewindowStyleApplied = new EventEmitter();
+
+ timewindowStyleFormGroup: UntypedFormGroup;
+
+ previewTimewindowStyle: TimewindowStyle;
+
+ constructor(private fb: UntypedFormBuilder,
+ protected store: Store) {
+ super(store);
+ }
+
+ ngOnInit(): void {
+ const computedTimewindowStyle = {...defaultTimewindowStyle, ...(this.timewindowStyle || {})};
+ this.timewindowStyleFormGroup = this.fb.group(
+ {
+ showIcon: [computedTimewindowStyle.showIcon, []],
+ iconSize: [computedTimewindowStyle.iconSize, []],
+ icon: [computedTimewindowStyle.icon, []],
+ iconPosition: [computedTimewindowStyle.iconPosition, []],
+ font: [computedTimewindowStyle.font, []],
+ color: [computedTimewindowStyle.color, []]
+ }
+ );
+ this.updatePreviewStyle(this.timewindowStyle);
+ this.updateTimewindowStyleEnabledState();
+ this.timewindowStyleFormGroup.valueChanges.subscribe((timewindowStyle: TimewindowStyle) => {
+ if (this.timewindowStyleFormGroup.valid) {
+ this.updatePreviewStyle(timewindowStyle);
+ setTimeout(() => {this.popover?.updatePosition();}, 0);
+ }
+ });
+ this.timewindowStyleFormGroup.get('showIcon').valueChanges.subscribe(() => {
+ this.updateTimewindowStyleEnabledState();
+ });
+ }
+
+ cancel() {
+ this.popover?.hide();
+ }
+
+ applyTimewindowStyle() {
+ const timewindowStyle = this.timewindowStyleFormGroup.getRawValue();
+ this.timewindowStyleApplied.emit(timewindowStyle);
+ }
+
+ private updateTimewindowStyleEnabledState() {
+ const showIcon: boolean = this.timewindowStyleFormGroup.get('showIcon').value;
+ if (showIcon) {
+ this.timewindowStyleFormGroup.get('iconSize').enable({emitEvent: false});
+ this.timewindowStyleFormGroup.get('icon').enable({emitEvent: false});
+ this.timewindowStyleFormGroup.get('iconPosition').enable({emitEvent: false});
+ } else {
+ this.timewindowStyleFormGroup.get('iconSize').disable({emitEvent: false});
+ this.timewindowStyleFormGroup.get('icon').disable({emitEvent: false});
+ this.timewindowStyleFormGroup.get('iconPosition').disable({emitEvent: false});
+ }
+ }
+
+ private updatePreviewStyle(timewindowStyle: TimewindowStyle) {
+ this.previewTimewindowStyle = deepClone(timewindowStyle);
+ }
+
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.html b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.html
new file mode 100644
index 0000000000..2bd981fffc
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.html
@@ -0,0 +1,25 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.ts
new file mode 100644
index 0000000000..ad6d3e1b85
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.ts
@@ -0,0 +1,97 @@
+///
+/// Copyright © 2016-2023 The Thingsboard Authors
+///
+/// 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.
+///
+
+import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { TimewindowStyle } from '@shared/models/widget-settings.models';
+import { MatButton } from '@angular/material/button';
+import { TbPopoverService } from '@shared/components/popover.service';
+import { Timewindow } from '@shared/models/time/time.models';
+import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component';
+
+@Component({
+ selector: 'tb-timewindow-style',
+ templateUrl: './timewindow-style.component.html',
+ styleUrls: [],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TimewindowStyleComponent),
+ multi: true
+ }
+ ]
+})
+export class TimewindowStyleComponent implements OnInit, ControlValueAccessor {
+
+ @Input()
+ disabled: boolean;
+
+ @Input()
+ previewValue: Timewindow;
+
+ private modelValue: TimewindowStyle;
+
+ private propagateChange = null;
+
+ constructor(private popoverService: TbPopoverService,
+ private renderer: Renderer2,
+ private viewContainerRef: ViewContainerRef) {}
+
+ ngOnInit(): void {
+ }
+
+ registerOnChange(fn: any): void {
+ this.propagateChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ writeValue(value: TimewindowStyle): void {
+ this.modelValue = value;
+ }
+
+ openTimewindowStylePopup($event: Event, matButton: MatButton) {
+ if ($event) {
+ $event.stopPropagation();
+ }
+ const trigger = matButton._elementRef.nativeElement;
+ if (this.popoverService.hasPopover(trigger)) {
+ this.popoverService.hidePopover(trigger);
+ } else {
+ const ctx: any = {
+ timewindowStyle: this.modelValue,
+ previewValue: this.previewValue
+ };
+ const timewindowStylePanelPopover = 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);
+ });
+ }
+ }
+
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts
index 392a829955..ea0d27dcf6 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts
@@ -30,6 +30,8 @@ import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widge
import { WidgetSettingsComponent } from '@home/components/widget/config/widget-settings.component';
import { TimewindowConfigPanelComponent } from '@home/components/widget/config/timewindow-config-panel.component';
import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module';
+import { TimewindowStyleComponent } from '@home/components/widget/config/timewindow-style.component';
+import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component';
@NgModule({
declarations:
@@ -43,6 +45,8 @@ import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings
DatasourcesComponent,
EntityAliasSelectComponent,
FilterSelectComponent,
+ TimewindowStyleComponent,
+ TimewindowStylePanelComponent,
TimewindowConfigPanelComponent,
WidgetSettingsComponent
],
@@ -62,6 +66,8 @@ import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings
DatasourcesComponent,
EntityAliasSelectComponent,
FilterSelectComponent,
+ TimewindowStyleComponent,
+ TimewindowStylePanelComponent,
TimewindowConfigPanelComponent,
WidgetSettingsComponent,
WidgetSettingsCommonModule
diff --git a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts
index dc3e9c7ac2..3c20317b13 100644
--- a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts
@@ -20,8 +20,8 @@ import {
ComponentFactory,
ComponentRef, HostBinding,
Inject,
- Injector,
- OnDestroy,
+ Injector, NgModuleRef,
+ OnDestroy, Type,
ViewContainerRef
} from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
@@ -35,11 +35,13 @@ import {
} from '@home/components/widget/dialog/custom-dialog.component';
import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core';
+import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service';
export interface CustomDialogContainerData {
controller: (instance: CustomDialogComponent) => void;
data?: any;
- customComponentFactory: ComponentFactory;
+ customComponentType: Type;
+ customComponentModuleRef: NgModuleRef;
}
@Component({
@@ -77,7 +79,8 @@ export class CustomDialogContainerComponent extends DialogComponent>
+ private customModules: Array>;
constructor(
private translate: TranslateService,
@@ -56,12 +56,13 @@ export class CustomDialogService {
if (Array.isArray(this.customModules)) {
modules.push(...this.customModules);
}
- return this.dynamicComponentFactoryService.createDynamicComponentFactory(
+ return this.dynamicComponentFactoryService.createDynamicComponent(
class CustomDialogComponentInstance extends CustomDialogComponent {}, template, modules).pipe(
- mergeMap((factory) => {
+ mergeMap((componentData) => {
const dialogData: CustomDialogContainerData = {
controller,
- customComponentFactory: factory,
+ customComponentType: componentData.componentType,
+ customComponentModuleRef: componentData.componentModuleRef,
data
};
let dialogConfig: MatDialogConfig = {
@@ -76,7 +77,7 @@ export class CustomDialogService {
CustomDialogContainerComponent,
dialogConfig).afterClosed().pipe(
tap(() => {
- this.dynamicComponentFactoryService.destroyDynamicComponentFactory(factory);
+ this.dynamicComponentFactoryService.destroyDynamicComponent(componentData.componentType);
})
);
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
index 22fac750b5..feeb1fe802 100644
--- a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
@@ -15,7 +15,7 @@
///
import { PageComponent } from '@shared/components/page.component';
-import { Directive, Injector, OnDestroy, OnInit } from '@angular/core';
+import { Directive, Injector, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models';
@@ -67,7 +67,8 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
@TbInject(UntypedFormBuilder) public fb: UntypedFormBuilder,
@TbInject(Injector) public readonly $injector: Injector,
@TbInject('widgetContext') public readonly ctx: WidgetContext,
- @TbInject('errorMessages') public readonly errorMessages: string[]) {
+ @TbInject('errorMessages') public readonly errorMessages: string[],
+ @TbInject('widgetTitlePanel') public readonly widgetTitlePanel: TemplateRef) {
super(store);
this.ctx.$injector = $injector;
this.ctx.deviceService = $injector.get(DeviceService);
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
index f799ed43ff..234443855f 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
@@ -37,16 +37,7 @@ import { DataKey, WidgetActionDescriptor, WidgetConfig } from '@shared/models/wi
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
-import {
- createLabelFromDatasource,
- deepClone,
- hashCode,
- isDefined,
- isDefinedAndNotNull,
- isNumber,
- isObject,
- isUndefined
-} from '@core/utils';
+import { deepClone, hashCode, isDefined, isDefinedAndNotNull, isNumber, isObject, isUndefined } from '@core/utils';
import cssjs from '@core/css/css';
import { sortItems } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order';
@@ -194,8 +185,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private subscription: IWidgetSubscription;
private widgetResize$: ResizeObserver;
- private alarmsTitlePattern: string;
-
private displayActivity = false;
private displayDetails = true;
public allowAcknowledgment = true;
@@ -322,7 +311,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
}
public onDataUpdated() {
- this.updateTitle(true);
this.alarmsDatasource.updateAlarms();
this.clearCache();
this.ctx.detectChanges();
@@ -342,13 +330,11 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.allowAssign = isDefined(this.settings.allowAssign) ? this.settings.allowAssign : true;
if (this.settings.alarmsTitle && this.settings.alarmsTitle.length) {
- this.alarmsTitlePattern = this.utils.customTranslation(this.settings.alarmsTitle, this.settings.alarmsTitle);
+ this.ctx.widgetTitle = this.settings.alarmsTitle;
} else {
- this.alarmsTitlePattern = this.translate.instant('alarm.alarms');
+ this.ctx.widgetTitle = this.translate.instant('alarm.alarms');
}
- this.updateTitle(false);
-
this.enableSelection = isDefined(this.settings.enableSelection) ? this.settings.enableSelection : true;
if (!this.allowAcknowledgment && !this.allowClear) {
this.enableSelection = false;
@@ -394,16 +380,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
$(this.elementRef.nativeElement).addClass(namespace);
}
- private updateTitle(updateWidgetParams = false) {
- const newTitle = createLabelFromDatasource(this.subscription.alarmSource, this.alarmsTitlePattern);
- if (this.ctx.widgetTitle !== newTitle) {
- this.ctx.widgetTitle = newTitle;
- if (updateWidgetParams) {
- this.ctx.updateWidgetParams();
- }
- }
- }
-
private updateAlarmSource() {
if (this.enableSelection) {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html
index 8c79c0e1e7..fee7dc5259 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html
@@ -17,6 +17,9 @@
-->
+
+
+
@@ -60,7 +63,7 @@
{{ icon }}
- {{ label }}
+ {{ label$ | async }}
{{ dateFormat.formatted }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss
index d0cf0ea0b7..05bbbb0bf0 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss
@@ -44,6 +44,13 @@
bottom: 12px;
right: 12px;
}
+ > div.tb-value-card-title-panel {
+ position: absolute;
+ top: 12px;
+ left: 12px;
+ right: 12px;
+ z-index: 2;
+ }
.tb-value-card-icon-row {
display: flex;
flex-direction: row;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts
index ac2cb62fee..48d432bcb7 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts
@@ -14,7 +14,7 @@
/// limitations under the License.
///
-import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
+import { ChangeDetectorRef, Component, Input, OnInit, TemplateRef } from '@angular/core';
import { WidgetContext } from '@home/models/widget-component.models';
import { formatValue, isDefinedAndNotNull } from '@core/utils';
import { DatePipe } from '@angular/common';
@@ -31,6 +31,7 @@ import {
} from '@shared/models/widget-settings.models';
import { valueCardDefaultSettings, ValueCardLayout, ValueCardWidgetSettings } from './value-card-widget.models';
import { WidgetComponent } from '@home/components/widget/widget.component';
+import { Observable } from 'rxjs';
@Component({
selector: 'tb-value-card-widget',
@@ -46,6 +47,9 @@ export class ValueCardWidgetComponent implements OnInit {
@Input()
ctx: WidgetContext;
+ @Input()
+ widgetTitlePanel: TemplateRef;
+
layout: ValueCardLayout;
showIcon = true;
icon = '';
@@ -53,7 +57,7 @@ export class ValueCardWidgetComponent implements OnInit {
iconColor: ColorProcessor;
showLabel = true;
- label = '';
+ label$: Observable;
labelStyle: ComponentStyle = {};
labelColor: ColorProcessor;
@@ -102,15 +106,16 @@ export class ValueCardWidgetComponent implements OnInit {
this.iconColor = ColorProcessor.fromSettings(this.settings.iconColor);
this.showLabel = this.settings.showLabel;
- this.label = getLabel(this.ctx.datasources);
- this.labelStyle = textStyle(this.settings.labelFont, '1.5', '0.25px');
+ const label = getLabel(this.ctx.datasources);
+ this.label$ = this.ctx.registerLabelPattern(label, this.label$);
+ this.labelStyle = textStyle(this.settings.labelFont, '0.25px');
this.labelColor = ColorProcessor.fromSettings(this.settings.labelColor);
- this.valueStyle = textStyle(this.settings.valueFont, '100%', '0.13px');
+ this.valueStyle = textStyle(this.settings.valueFont, '0.13px');
this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor);
this.showDate = this.settings.showDate;
this.dateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.dateFormat);
- this.dateStyle = textStyle(this.settings.dateFont, '1.33', '0.25px');
+ this.dateStyle = textStyle(this.settings.dateFont, '0.25px');
this.dateColor = ColorProcessor.fromSettings(this.settings.dateColor);
this.backgroundStyle = backgroundStyle(this.settings.background);
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts
index 549ba4abcd..aa442bd199 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts
@@ -89,7 +89,8 @@ export const valueCardDefaultSettings = (horizontal: boolean): ValueCardWidgetSe
size: 16,
sizeUnit: 'px',
style: 'normal',
- weight: '500'
+ weight: '500',
+ lineHeight: '1.5'
},
labelColor: constantColor('rgba(0, 0, 0, 0.87)'),
showIcon: true,
@@ -102,7 +103,8 @@ export const valueCardDefaultSettings = (horizontal: boolean): ValueCardWidgetSe
size: 52,
sizeUnit: 'px',
style: 'normal',
- weight: '500'
+ weight: '500',
+ lineHeight: '100%'
},
valueColor: constantColor('rgba(0, 0, 0, 0.87)'),
showDate: true,
@@ -112,7 +114,8 @@ export const valueCardDefaultSettings = (horizontal: boolean): ValueCardWidgetSe
size: 12,
sizeUnit: 'px',
style: 'normal',
- weight: '500'
+ weight: '500',
+ lineHeight: '1.33'
},
dateColor: constantColor('rgba(0, 0, 0, 0.38)'),
background: {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
index 07b05db0f2..ecb10f3ade 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
@@ -42,15 +42,7 @@ import {
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
-import {
- createLabelFromDatasource,
- deepClone,
- hashCode,
- isDefined,
- isNumber,
- isObject,
- isUndefined
-} from '@core/utils';
+import { deepClone, hashCode, isDefined, isNumber, isObject, isUndefined } from '@core/utils';
import cssjs from '@core/css/css';
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
@@ -164,8 +156,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private subscription: IWidgetSubscription;
private widgetResize$: ResizeObserver;
- private entitiesTitlePattern: string;
-
private defaultPageSize = 10;
private defaultSortOrder = 'entityName';
@@ -266,7 +256,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
}
public onDataUpdated() {
- this.updateTitle(true);
this.entityDatasource.dataUpdated();
this.clearCache();
this.ctx.detectChanges();
@@ -281,16 +270,15 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
this.setCellButtonAction = !!this.ctx.actionsApi.getActionDescriptors('actionCellButton').length;
- this.hasRowAction = !!this.ctx.actionsApi.getActionDescriptors('rowClick').length || !!this.ctx.actionsApi.getActionDescriptors('rowDoubleClick').length;
+ this.hasRowAction = !!this.ctx.actionsApi.getActionDescriptors('rowClick').length ||
+ !!this.ctx.actionsApi.getActionDescriptors('rowDoubleClick').length;
if (this.settings.entitiesTitle && this.settings.entitiesTitle.length) {
- this.entitiesTitlePattern = this.utils.customTranslation(this.settings.entitiesTitle, this.settings.entitiesTitle);
+ this.ctx.widgetTitle = this.settings.entitiesTitle;
} else {
- this.entitiesTitlePattern = this.translate.instant('entity.entities');
+ this.ctx.widgetTitle = this.translate.instant('entity.entities');
}
- this.updateTitle(false);
-
this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
@@ -319,16 +307,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
$(this.elementRef.nativeElement).addClass(namespace);
}
- private updateTitle(updateWidgetParams = false) {
- const newTitle = createLabelFromDatasource(this.subscription.datasources[0], this.entitiesTitlePattern);
- if (this.ctx.widgetTitle !== newTitle) {
- this.ctx.widgetTitle = newTitle;
- if (updateWidgetParams) {
- this.ctx.updateWidgetParams();
- }
- }
- }
-
private updateDatasources() {
const displayEntityName = isDefined(this.settings.displayEntityName) ? this.settings.displayEntityName : true;
@@ -498,14 +476,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
overlayRef.dispose();
});
- const columns: DisplayColumn[] = this.columns.map(column => {
- return {
+ const columns: DisplayColumn[] = this.columns.map(column => ({
title: column.title,
def: column.def,
display: this.displayedColumns.indexOf(column.def) > -1,
selectable: this.columnSelectionAvailability[column.def]
- };
- });
+ }));
const providers: StaticProvider[] = [
{
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts
index 20cce79457..2e9a9fb5f9 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts
@@ -94,12 +94,8 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
private initializeConfig() {
if (this.settings.widgetTitle && this.settings.widgetTitle.length) {
- const title = createLabelFromDatasource(this.datasource, this.settings.widgetTitle);
- this.ctx.widgetTitle = this.utils.customTranslation(title, title);
- } else {
- this.ctx.widgetTitle = this.ctx.widgetConfig.title;
+ this.ctx.widgetTitle = this.settings.widgetTitle;
}
-
if (this.settings.labelValue && this.settings.labelValue.length) {
const label = createLabelFromDatasource(this.datasource, this.settings.labelValue);
this.labelValue = this.utils.customTranslation(label, label);
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts
index 6f29a495cf..f667c785c1 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts
@@ -202,10 +202,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
private initializeConfig() {
if (this.settings.widgetTitle && this.settings.widgetTitle.length) {
- const titlePatternText = this.utils.customTranslation(this.settings.widgetTitle, this.settings.widgetTitle);
- this.ctx.widgetTitle = createLabelFromDatasource(this.datasources[0], titlePatternText);
- } else {
- this.ctx.widgetTitle = this.ctx.widgetConfig.title;
+ this.ctx.widgetTitle = this.settings.widgetTitle;
}
this.settings.groupTitle = this.settings.groupTitle || '${entityName}';
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html
index 71a9b60118..f0fd9cdb16 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html
@@ -44,12 +44,13 @@
{{ 'widgets.value-card.icon' | translate }}
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts
index 3d2658dae1..61a6bc4865 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts
@@ -67,9 +67,9 @@ export class CssUnitSelectComponent implements OnInit, ControlValueAccessor {
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
- this.cssUnitFormControl.disable();
+ this.cssUnitFormControl.disable({emitEvent: false});
} else {
- this.cssUnitFormControl.enable();
+ this.cssUnitFormControl.enable({emitEvent: false});
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts
index 6393a92054..e4f0b47946 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts
@@ -90,9 +90,9 @@ export class DateFormatSelectComponent implements OnInit, ControlValueAccessor {
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
- this.dateFormatFormControl.disable();
+ this.dateFormatFormControl.disable({emitEvent: false});
} else {
- this.dateFormatFormControl.enable();
+ this.dateFormatFormControl.enable({emitEvent: false});
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html
index 36d488d766..9fe3273f74 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html
@@ -72,6 +72,12 @@
+
widgets.widget-font.preview
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts
index 2f86394f44..a0e77ef380 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts
@@ -101,7 +101,8 @@ export class FontSettingsPanelComponent extends PageComponent implements OnInit
sizeUnit: [(this.font?.sizeUnit || 'px'), []],
family: [this.font?.family, []],
weight: [this.font?.weight, []],
- style: [this.font?.style, []]
+ style: [this.font?.style, []],
+ lineHeight: [this.font?.lineHeight, []]
}
);
this.updatePreviewStyle(this.font);
@@ -146,7 +147,7 @@ export class FontSettingsPanelComponent extends PageComponent implements OnInit
}
private updatePreviewStyle(font: Font) {
- this.previewStyle = {...(this.initialPreviewStyle || {}), ...textStyle(font, '1')};
+ this.previewStyle = {...(this.initialPreviewStyle || {}), ...textStyle(font)};
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
index d3dbb9853c..1b73ccbe4a 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
@@ -227,7 +227,7 @@ export class WidgetComponentService {
}
public clearWidgetInfo(widgetInfo: WidgetInfo, bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): void {
- this.dynamicComponentFactoryService.destroyDynamicComponentFactory(widgetInfo.componentFactory);
+ this.dynamicComponentFactoryService.destroyDynamicComponent(widgetInfo.componentType);
this.widgetService.deleteWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem);
}
@@ -362,13 +362,14 @@ export class WidgetComponentService {
return of(resolvedModules);
} else {
this.registerWidgetSettingsForms(widgetInfo, resolvedModules.factories);
- return this.dynamicComponentFactoryService.createDynamicComponentFactory(
+ return this.dynamicComponentFactoryService.createDynamicComponent(
class DynamicWidgetComponentInstance extends DynamicWidgetComponent {},
widgetInfo.templateHtml,
resolvedModules.modules
).pipe(
- map((factory) => {
- widgetInfo.componentFactory = factory;
+ map((componentData) => {
+ widgetInfo.componentType = componentData.componentType;
+ widgetInfo.componentModuleRef = componentData.componentModuleRef;
return null;
}),
catchError(e => {
@@ -546,8 +547,8 @@ export class WidgetComponentService {
if (isUndefined(result.typeParameters.previewHeight)) {
result.typeParameters.previewHeight = '70%';
}
- if (isUndefined(result.typeParameters.absoluteHeader)) {
- result.typeParameters.absoluteHeader = false;
+ if (isUndefined(result.typeParameters.embedTitlePanel)) {
+ result.typeParameters.embedTitlePanel = false;
}
if (isFunction(widgetTypeInstance.actionSources)) {
result.actionSources = widgetTypeInstance.actionSources();
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
index 5f54970859..23440260d4 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
@@ -70,6 +70,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts
index da35390ffd..2bdf662469 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts
@@ -369,7 +369,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
this.dataSettings.addControl('timewindowConfig', this.fb.control({
useDashboardTimewindow: true,
displayTimewindow: true,
- timewindow: null
+ timewindow: null,
+ timewindowStyle: null
}));
if (this.widgetType === widgetType.alarm) {
this.dataSettings.addControl('alarmFilterConfig', this.fb.control(null));
@@ -517,7 +518,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
useDashboardTimewindow,
displayTimewindow: isDefined(config.displayTimewindow) ?
config.displayTimewindow : true,
- timewindow: config.timewindow
+ timewindow: config.timewindow,
+ timewindowStyle: config.timewindowStyle
}, {emitEvent: false});
}
if (this.modelValue.isDataEnabled) {
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html
index 43b54884ec..1e3fd33171 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html
@@ -32,34 +32,13 @@
(click)="onClicked($event)"
(contextmenu)="onContextMenu($event)">
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss
index 52caeb2a5c..a61dc463db 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss
@@ -37,13 +37,6 @@ div.tb-widget {
flex-direction: row;
place-content: flex-start space-between;
align-items: flex-start;
- &-absolute {
- position: absolute;
- top: 0;
- right: 0;
- left: 0;
- z-index: 1;
- }
}
.tb-widget-title {
@@ -59,23 +52,24 @@ div.tb-widget {
tb-timewindow {
font-size: 14px;
opacity: .85;
- margin: 0;
}
- .title {
+ .title-row {
+ display: flex;
+ flex-direction: row;
+ place-content: center flex-start;
+ align-items: center;
+ gap: 4px;
width: 100%;
+ }
+
+ .title {
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
line-height: 24px;
- letter-spacing: .01em;
+ letter-spacing: normal;
margin: 0;
- display: -webkit-box;
- -webkit-box-orient: vertical;
- -webkit-line-clamp: 2;
-
- &.single-row{
- -webkit-line-clamp: 1;
- }
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.ts
index 41f3fe7e4f..bc298734fc 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.ts
@@ -77,13 +77,15 @@ export class WidgetPreviewComponent extends PageComponent implements OnInit, OnC
}
private loadPreviewWidget() {
- const widget = deepClone(this.widget);
- widget.sizeX = 24;
- widget.sizeY = this.widget.sizeY * 2;
- widget.row = 0;
- widget.col = 0;
- widget.config = this.widgetConfig;
- this.widgets = [widget];
+ if (this.widget) {
+ const widget = deepClone(this.widget);
+ widget.sizeX = 24;
+ widget.sizeY = this.widget.sizeY * 2;
+ widget.row = 0;
+ widget.col = 0;
+ widget.config = this.widgetConfig;
+ this.widgets = [widget];
+ }
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts
index c276999d83..fe8bcd78c1 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts
@@ -32,6 +32,7 @@ import {
Optional,
Renderer2,
SimpleChanges,
+ TemplateRef,
Type,
ViewChild,
ViewContainerRef,
@@ -125,6 +126,9 @@ import { IModulesMap } from '@modules/common/modules-map.models';
})
export class WidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
+ @Input()
+ widgetTitlePanel: TemplateRef
;
+
@Input()
isEdit: boolean;
@@ -308,9 +312,6 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.loadFromWidgetInfo();
}
);
- setTimeout(() => {
- this.dashboardWidget.updateWidgetParams();
- }, 0);
const noDataDisplayMessage = this.widget.config.noDataDisplayMessage;
if (isNotEmptyStr(noDataDisplayMessage)) {
@@ -387,7 +388,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.handleWidgetException(e);
}
}
- this.widgetContext.destroyed = true;
+ this.widgetContext.destroy();
this.destroyDynamicWidgetComponent();
}
}
@@ -409,7 +410,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
elem.classList.add(this.widgetContext.widgetNamespace);
this.widgetType = this.widgetInfo.widgetTypeFunction;
this.typeParameters = this.widgetInfo.typeParameters;
- this.widgetContext.absoluteHeader = this.typeParameters.absoluteHeader;
+ this.widgetContext.embedTitlePanel = this.typeParameters.embedTitlePanel;
if (!this.widgetType) {
this.widgetTypeInstance = {};
@@ -480,6 +481,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
}
if (!this.widgetContext.inited && this.isReady()) {
this.widgetContext.inited = true;
+ this.dashboardWidget.updateWidgetParams();
this.widgetContext.detectContainerChanges();
if (this.cafs.init) {
this.cafs.init();
@@ -493,7 +495,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
if (this.dataUpdatePending) {
this.widgetTypeInstance.onDataUpdated();
setTimeout(() => {
- this.dashboardWidget.updateCustomHeaderActions(true);
+ this.dashboardWidget.updateParamsFromData(true);
}, 0);
this.dataUpdatePending = false;
}
@@ -735,6 +737,10 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
{
provide: 'errorMessages',
useValue: this.errorMessages
+ },
+ {
+ provide: 'widgetTitlePanel',
+ useValue: this.widgetTitlePanel
}
],
parent: this.injector
@@ -745,7 +751,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.widgetContext.$containerParent = $(containerElement);
try {
- this.dynamicWidgetComponentRef = this.widgetContentContainer.createComponent(this.widgetInfo.componentFactory, 0, injector);
+ this.dynamicWidgetComponentRef = this.widgetContentContainer.createComponent(this.widgetInfo.componentType,
+ {index: 0, injector, ngModuleRef: this.widgetInfo.componentModuleRef});
this.cd.detectChanges();
} catch (e) {
if (this.dynamicWidgetComponentRef) {
@@ -845,7 +852,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
if (this.widgetInstanceInited) {
this.widgetTypeInstance.onDataUpdated();
setTimeout(() => {
- this.dashboardWidget.updateCustomHeaderActions(true);
+ this.dashboardWidget.updateParamsFromData(true);
}, 0);
} else {
this.dataUpdatePending = true;
diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
index 5bae1ad256..8104e80e36 100644
--- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
+++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts
@@ -33,7 +33,7 @@ import { IAliasController, IStateController } from '@app/core/api/widget-api.mod
import { enumerable } from '@shared/decorators/enumerable';
import { UtilsService } from '@core/services/utils.service';
import { TbPopoverComponent } from '@shared/components/popover.component';
-import { ComponentStyle, textStyle } from '@shared/models/widget-settings.models';
+import { ComponentStyle, iconStyle, textStyle } from '@shared/models/widget-settings.models';
export interface WidgetsData {
widgets: Array;
@@ -331,8 +331,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
margin: string;
borderRadius: string;
- title: string;
- customTranslatedTitle: string;
+ title$: Observable;
titleTooltip: string;
showTitle: boolean;
titleStyle: ComponentStyle;
@@ -431,30 +430,23 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
this.margin = this.widget.config.margin || '0px';
this.borderRadius = this.widget.config.borderRadius;
- this.title = isDefined(this.widgetContext.widgetTitle)
+ const title = isDefined(this.widgetContext.widgetTitle)
&& this.widgetContext.widgetTitle.length ? this.widgetContext.widgetTitle : this.widget.config.title;
- this.customTranslatedTitle = this.dashboard.utils.customTranslation(this.title, this.title);
+ this.title$ = this.widgetContext.registerLabelPattern(title, this.title$);
this.titleTooltip = isDefined(this.widgetContext.widgetTitleTooltip)
&& this.widgetContext.widgetTitleTooltip.length ? this.widgetContext.widgetTitleTooltip : this.widget.config.titleTooltip;
this.titleTooltip = this.dashboard.utils.customTranslation(this.titleTooltip, this.titleTooltip);
this.showTitle = isDefined(this.widget.config.showTitle) ? this.widget.config.showTitle : true;
- this.titleStyle = {...(this.widget.config.titleStyle || {}), ...textStyle(this.widget.config.titleFont, '24px', '0.01em')};
+ this.titleStyle = {...(this.widget.config.titleStyle || {}), ...textStyle(this.widget.config.titleFont, 'normal')};
if (this.widget.config.titleColor) {
this.titleStyle.color = this.widget.config.titleColor;
}
this.titleIcon = isDefined(this.widget.config.titleIcon) ? this.widget.config.titleIcon : '';
this.showTitleIcon = isDefined(this.widget.config.showTitleIcon) ? this.widget.config.showTitleIcon : false;
- this.titleIconStyle = {};
+ this.titleIconStyle = this.widget.config.iconSize ? iconStyle(this.widget.config.iconSize) : {};
if (this.widget.config.iconColor) {
this.titleIconStyle.color = this.widget.config.iconColor;
}
- if (this.widget.config.iconSize) {
- this.titleIconStyle.width = this.widget.config.iconSize;
- this.titleIconStyle.height = this.widget.config.iconSize;
- this.titleIconStyle.fontSize = this.widget.config.iconSize;
- this.titleIconStyle.lineHeight = this.widget.config.iconSize;
- }
-
this.dropShadow = isDefined(this.widget.config.dropShadow) ? this.widget.config.dropShadow : true;
this.enableFullscreen = isDefined(this.widget.config.enableFullscreen) ? this.widget.config.enableFullscreen : true;
@@ -497,14 +489,22 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
this.showWidgetActions = !this.widgetContext.hideTitlePanel;
- this.updateCustomHeaderActions();
+ this.updateParamsFromData();
this.widgetActions = this.widgetContext.widgetActions ? this.widgetContext.widgetActions : [];
if (detectChanges) {
this.widgetContext.detectContainerChanges();
}
}
- updateCustomHeaderActions(detectChanges = false) {
+ updateParamsFromData(detectChanges = false) {
+ this.widgetContext.updateLabelPatterns();
+ const update = this.updateCustomHeaderActions();
+ if (update && detectChanges) {
+ this.widgetContext.detectContainerChanges();
+ }
+ }
+
+ private updateCustomHeaderActions(): boolean {
let customHeaderActions: Array;
if (this.widgetContext.customHeaderActions) {
let data: FormattedData[] = [];
@@ -517,10 +517,9 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget {
}
if (!isEqual(this.customHeaderActions, customHeaderActions)) {
this.customHeaderActions = customHeaderActions;
- if (detectChanges) {
- this.widgetContext.detectContainerChanges();
- }
+ return true;
}
+ return false;
}
private filterCustomHeaderAction(action: WidgetHeaderAction, data: FormattedData[]): boolean {
diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts
index 18c8ccd19e..12d3312dbb 100644
--- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts
+++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts
@@ -45,13 +45,13 @@ import {
WidgetActionsApi,
WidgetSubscriptionApi
} from '@core/api/widget-api.models';
-import { ChangeDetectorRef, ComponentFactory, Injector, NgZone, Type } from '@angular/core';
+import { ChangeDetectorRef, Injector, NgModuleRef, NgZone, Type } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service';
import { WidgetTypeId } from '@shared/models/id/widget-type-id';
import { TenantId } from '@shared/models/id/tenant-id';
import { WidgetLayout } from '@shared/models/dashboard.models';
-import { formatValue, isDefined } from '@core/utils';
+import { createLabelFromDatasource, formatValue, hasDatasourceLabelsVariables, isDefined } from '@core/utils';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import {
@@ -87,10 +87,12 @@ import * as RxJS from 'rxjs';
import * as RxJSOperators from 'rxjs/operators';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { EntityId } from '@shared/models/id/entity-id';
-import { AlarmQuery, AlarmSearchStatus, AlarmStatus} from '@app/shared/models/alarm.models';
+import { AlarmQuery, AlarmSearchStatus, AlarmStatus } from '@app/shared/models/alarm.models';
import { MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api';
import { UserId } from '@shared/models/id/user-id';
import { UserSettingsService } from '@core/http/user-settings.service';
+import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service';
+import { BehaviorSubject, Observable } from 'rxjs';
export interface IWidgetAction {
name: string;
@@ -200,6 +202,8 @@ export class WidgetContext {
subscriptions: {[id: string]: IWidgetSubscription} = {};
defaultSubscription: IWidgetSubscription = null;
+ labelPatterns = new Map, LabelVariablePattern>();
+
timewindowFunctions: TimewindowFunctions = {
onUpdateTimewindow: (startTimeMs, endTimeMs, interval) => {
if (this.defaultSubscription) {
@@ -265,7 +269,7 @@ export class WidgetContext {
hiddenData?: Array<{data: DataSet}>;
timeWindow?: WidgetTimewindow;
- absoluteHeader?: boolean;
+ embedTitlePanel?: boolean;
hideTitlePanel = false;
@@ -312,6 +316,23 @@ export class WidgetContext {
});
}
+ registerLabelPattern(label: string, label$: Observable): Observable {
+ let labelPattern = label$ ? this.labelPatterns.get(label$) : null;
+ if (labelPattern) {
+ labelPattern.setupPattern(label);
+ } else {
+ labelPattern = new LabelVariablePattern(label, this);
+ this.labelPatterns.set(labelPattern.label$, labelPattern);
+ }
+ return labelPattern.label$;
+ }
+
+ updateLabelPatterns() {
+ for (const labelPattern of this.labelPatterns.values()) {
+ labelPattern.update();
+ }
+ }
+
showSuccessToast(message: string, duration: number = 1000,
verticalPosition: NotificationVerticalPosition = 'bottom',
horizontalPosition: NotificationHorizontalPosition = 'left',
@@ -406,6 +427,14 @@ export class WidgetContext {
this.widgetActions = undefined;
}
+ destroy() {
+ for (const labelPattern of this.labelPatterns.values()) {
+ labelPattern.destroy();
+ }
+ this.labelPatterns.clear();
+ this.destroyed = true;
+ }
+
closeDialog(resultData: any = null) {
const dialogRef = this.$scope.dialogRef || this.stateController.dashboardCtrl.dashboardCtx.getDashboard().dialogRef;
if (dialogRef) {
@@ -426,6 +455,42 @@ export class WidgetContext {
}
}
+export class LabelVariablePattern {
+
+ private pattern: string;
+ private hasVariables: boolean;
+
+ private labelSubject = new BehaviorSubject('');
+
+ public label$ = this.labelSubject.asObservable();
+
+ constructor(label: string,
+ private ctx: WidgetContext) {
+ this.setupPattern(label);
+ }
+
+ setupPattern(label: string) {
+ this.pattern = this.ctx.dashboard.utils.customTranslation(label, label);
+ this.hasVariables = hasDatasourceLabelsVariables(this.pattern);
+ this.update();
+ }
+
+ update() {
+ let label = this.pattern;
+ const datasource = this.ctx.defaultSubscription?.firstDatasource;
+ if (this.hasVariables && datasource) {
+ label = createLabelFromDatasource(datasource, label);
+ }
+ if (this.labelSubject.value !== label) {
+ this.labelSubject.next(label);
+ }
+ }
+
+ destroy() {
+ this.labelSubject.complete();
+ }
+}
+
export interface IDynamicWidgetComponent {
readonly ctx: WidgetContext;
readonly errorMessages: string[];
@@ -446,7 +511,8 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri
typeLatestDataKeySettingsSchema?: string | any;
image?: string;
description?: string;
- componentFactory?: ComponentFactory;
+ componentType?: Type;
+ componentModuleRef?: NgModuleRef;
}
export interface WidgetConfigComponentData {
diff --git a/ui-ngx/src/app/shared/components/color-input.component.ts b/ui-ngx/src/app/shared/components/color-input.component.ts
index 2f0b7b1dae..834fcb8176 100644
--- a/ui-ngx/src/app/shared/components/color-input.component.ts
+++ b/ui-ngx/src/app/shared/components/color-input.component.ts
@@ -153,10 +153,10 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro
$event.stopPropagation();
this.dialogs.colorPicker(this.colorFormGroup.get('color').value,
this.colorClearButton).subscribe(
- (color) => {
- if (color) {
+ (result) => {
+ if (!result?.canceled) {
this.colorFormGroup.patchValue(
- {color}, {emitEvent: true}
+ {color: result?.color}, {emitEvent: true}
);
this.cd.markForCheck();
}
diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts
index 06d24f026b..38fcedf406 100644
--- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts
+++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts
@@ -26,12 +26,17 @@ export interface ColorPickerDialogData {
colorClearButton: boolean;
}
+export interface ColorPickerDialogResult {
+ color?: string;
+ canceled?: boolean;
+}
+
@Component({
selector: 'tb-color-picker-dialog',
templateUrl: './color-picker-dialog.component.html',
styleUrls: ['./color-picker-dialog.component.scss']
})
-export class ColorPickerDialogComponent extends DialogComponent {
+export class ColorPickerDialogComponent extends DialogComponent {
color: string;
colorClearButton: boolean;
@@ -39,18 +44,18 @@ export class ColorPickerDialogComponent extends DialogComponent,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData,
- public dialogRef: MatDialogRef) {
+ public dialogRef: MatDialogRef) {
super(store, router, dialogRef);
this.color = data.color;
this.colorClearButton = data.colorClearButton;
}
selectColor(color: string) {
- this.dialogRef.close(color);
+ this.dialogRef.close({color});
}
cancel(): void {
- this.dialogRef.close(null);
+ this.dialogRef.close({canceled: true});
}
}
diff --git a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html
index 1051be6525..632e9216be 100644
--- a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html
+++ b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html
@@ -23,6 +23,7 @@
close
diff --git a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts
index b6321c966d..53aec6d74f 100644
--- a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts
+++ b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts
@@ -23,6 +23,12 @@ import { DialogComponent } from '@shared/components/dialog.component';
export interface MaterialIconsDialogData {
icon: string;
+ iconClearButton: boolean;
+}
+
+export interface MaterialIconsDialogResult {
+ icon?: string;
+ canceled?: boolean;
}
@Component({
@@ -31,24 +37,26 @@ export interface MaterialIconsDialogData {
providers: [],
styleUrls: ['./material-icons-dialog.component.scss']
})
-export class MaterialIconsDialogComponent extends DialogComponent
{
+export class MaterialIconsDialogComponent extends DialogComponent {
selectedIcon: string;
+ iconClearButton: boolean;
constructor(protected store: Store,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: MaterialIconsDialogData,
- public dialogRef: MatDialogRef) {
+ public dialogRef: MatDialogRef) {
super(store, router, dialogRef);
this.selectedIcon = data.icon;
+ this.iconClearButton = data.iconClearButton;
}
selectIcon(icon: string) {
- this.dialogRef.close(icon);
+ this.dialogRef.close({icon});
}
cancel(): void {
- this.dialogRef.close(null);
+ this.dialogRef.close({canceled: true});
}
}
diff --git a/ui-ngx/src/app/shared/components/icon.component.ts b/ui-ngx/src/app/shared/components/icon.component.ts
index d1e2c6ddcd..fcc7b70081 100644
--- a/ui-ngx/src/app/shared/components/icon.component.ts
+++ b/ui-ngx/src/app/shared/components/icon.component.ts
@@ -120,7 +120,7 @@ export class TbIconComponent extends _TbIconBase
this._contentChanges = this.contentObserver.observe(this._iconNameContent.nativeElement)
.subscribe(() => {
const content = this.viewValue;
- if (content && this.icon !== content) {
+ if (this.icon !== content) {
this.icon = content;
this._updateIcon();
}
diff --git a/ui-ngx/src/app/shared/components/json-form/json-form.component.ts b/ui-ngx/src/app/shared/components/json-form/json-form.component.ts
index 6ed1d133ae..6da145d826 100644
--- a/ui-ngx/src/app/shared/components/json-form/json-form.component.ts
+++ b/ui-ngx/src/app/shared/components/json-form/json-form.component.ts
@@ -216,9 +216,9 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
private onColorClick(key: (string | number)[],
val: tinycolor.ColorFormats.RGBA,
colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) {
- this.dialogs.colorPicker(tinycolor(val).toRgbString()).subscribe((color) => {
- if (color && colorSelectedFn) {
- colorSelectedFn(tinycolor(color).toRgb());
+ this.dialogs.colorPicker(tinycolor(val).toRgbString()).subscribe((result) => {
+ if (!result?.canceled && colorSelectedFn) {
+ colorSelectedFn(tinycolor(result?.color).toRgb());
}
});
}
@@ -226,9 +226,9 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
private onIconClick(key: (string | number)[],
val: string,
iconSelectedFn: (icon: string) => void) {
- this.dialogs.materialIconPicker(val).subscribe((icon) => {
- if (icon && iconSelectedFn) {
- iconSelectedFn(icon);
+ this.dialogs.materialIconPicker(val).subscribe((result) => {
+ if (!result?.canceled && iconSelectedFn) {
+ iconSelectedFn(result?.icon);
}
});
}
diff --git a/ui-ngx/src/app/shared/components/markdown.component.ts b/ui-ngx/src/app/shared/components/markdown.component.ts
index cd03959fad..7936d2f51a 100644
--- a/ui-ngx/src/app/shared/components/markdown.component.ts
+++ b/ui-ngx/src/app/shared/components/markdown.component.ts
@@ -17,7 +17,6 @@
import {
ChangeDetectorRef,
Component,
- ComponentFactory,
ComponentRef,
ElementRef,
EventEmitter,
@@ -91,7 +90,7 @@ export class TbMarkdownComponent implements OnChanges {
error = null;
private tbMarkdownInstanceComponentRef: ComponentRef;
- private tbMarkdownInstanceComponentFactory: ComponentFactory;
+ private tbMarkdownInstanceComponentType: Type;
constructor(private help: HelpService,
private cd: ChangeDetectorRef,
@@ -153,7 +152,7 @@ export class TbMarkdownComponent implements OnChanges {
if (this.additionalCompileModules) {
compileModules = compileModules.concat(this.additionalCompileModules);
}
- this.dynamicComponentFactoryService.createDynamicComponentFactory(
+ this.dynamicComponentFactoryService.createDynamicComponent(
class TbMarkdownInstance {
ngOnDestroy(): void {
parent.destroyMarkdownInstanceResources();
@@ -162,12 +161,13 @@ export class TbMarkdownComponent implements OnChanges {
template,
compileModules,
true, 1, styles
- ).subscribe((factory) => {
- this.tbMarkdownInstanceComponentFactory = factory;
+ ).subscribe((componentData) => {
+ this.tbMarkdownInstanceComponentType = componentData.componentType;
const injector: Injector = Injector.create({providers: [], parent: this.markdownContainer.injector});
try {
this.tbMarkdownInstanceComponentRef =
- this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentFactory, 0, injector);
+ this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentType,
+ {index: 0, injector, ngModuleRef: componentData.componentModuleRef});
if (this.context) {
for (const propName of Object.keys(this.context)) {
this.tbMarkdownInstanceComponentRef.instance[propName] = this.context[propName];
@@ -261,9 +261,9 @@ export class TbMarkdownComponent implements OnChanges {
}
private destroyMarkdownInstanceResources() {
- if (this.tbMarkdownInstanceComponentFactory) {
- this.dynamicComponentFactoryService.destroyDynamicComponentFactory(this.tbMarkdownInstanceComponentFactory);
- this.tbMarkdownInstanceComponentFactory = null;
+ if (this.tbMarkdownInstanceComponentType) {
+ this.dynamicComponentFactoryService.destroyDynamicComponent(this.tbMarkdownInstanceComponentType);
+ this.tbMarkdownInstanceComponentType = null;
}
this.tbMarkdownInstanceComponentRef = null;
}
diff --git a/ui-ngx/src/app/shared/components/material-icon-select.component.ts b/ui-ngx/src/app/shared/components/material-icon-select.component.ts
index 740bb4545a..255aeb0668 100644
--- a/ui-ngx/src/app/shared/components/material-icon-select.component.ts
+++ b/ui-ngx/src/app/shared/components/material-icon-select.component.ts
@@ -54,17 +54,9 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit
@Input()
disabled: boolean;
- private iconClearButtonValue: boolean;
- get iconClearButton(): boolean {
- return this.iconClearButtonValue;
- }
@Input()
- set iconClearButton(value: boolean) {
- const newVal = coerceBooleanProperty(value);
- if (this.iconClearButtonValue !== newVal) {
- this.iconClearButtonValue = newVal;
- }
- }
+ @coerceBoolean()
+ iconClearButton = false;
private requiredValue: boolean;
get required(): boolean {
@@ -135,11 +127,12 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit
openIconDialog() {
if (!this.disabled) {
- this.dialogs.materialIconPicker(this.materialIconFormGroup.get('icon').value).subscribe(
- (icon) => {
- if (icon) {
+ this.dialogs.materialIconPicker(this.materialIconFormGroup.get('icon').value,
+ this.iconClearButton).subscribe(
+ (result) => {
+ if (!result?.canceled) {
this.materialIconFormGroup.patchValue(
- {icon}, {emitEvent: true}
+ {icon: result?.icon}, {emitEvent: true}
);
this.cd.markForCheck();
}
@@ -159,7 +152,8 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit
const materialIconsPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, MaterialIconsComponent, 'left', true, null,
{
- selectedIcon: this.materialIconFormGroup.get('icon').value
+ selectedIcon: this.materialIconFormGroup.get('icon').value,
+ iconClearButton: this.iconClearButton
},
{},
{}, {}, true);
diff --git a/ui-ngx/src/app/shared/components/material-icons.component.html b/ui-ngx/src/app/shared/components/material-icons.component.html
index d31a73ddb5..79476572f6 100644
--- a/ui-ngx/src/app/shared/components/material-icons.component.html
+++ b/ui-ngx/src/app/shared/components/material-icons.component.html
@@ -62,4 +62,13 @@
{{ 'icon.no-icons-found' | translate:{iconSearch: searchIconControl.value} }}
+
+
+
diff --git a/ui-ngx/src/app/shared/components/material-icons.component.scss b/ui-ngx/src/app/shared/components/material-icons.component.scss
index 23b959d118..8cf84e0687 100644
--- a/ui-ngx/src/app/shared/components/material-icons.component.scss
+++ b/ui-ngx/src/app/shared/components/material-icons.component.scss
@@ -58,4 +58,12 @@
margin: 0;
}
}
+ .tb-material-icons-panel-buttons {
+ height: 40px;
+ display: flex;
+ flex-direction: row;
+ gap: 16px;
+ align-items: flex-start;
+ align-self: flex-start;
+ }
}
diff --git a/ui-ngx/src/app/shared/components/material-icons.component.ts b/ui-ngx/src/app/shared/components/material-icons.component.ts
index 9347243af2..1d826816b7 100644
--- a/ui-ngx/src/app/shared/components/material-icons.component.ts
+++ b/ui-ngx/src/app/shared/components/material-icons.component.ts
@@ -36,6 +36,7 @@ import { ResourcesService } from '@core/services/resources.service';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { BreakpointObserver } from '@angular/cdk/layout';
import { MediaBreakpoints } from '@shared/models/constants';
+import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-material-icons',
@@ -52,6 +53,10 @@ export class MaterialIconsComponent extends PageComponent implements OnInit {
@Input()
selectedIcon: string;
+ @Input()
+ @coerceBoolean()
+ iconClearButton = false;
+
@Input()
popover: TbPopoverComponent;
@@ -123,6 +128,10 @@ export class MaterialIconsComponent extends PageComponent implements OnInit {
this.iconSelected.emit(icon.name);
}
+ clearIcon() {
+ this.iconSelected.emit(null);
+ }
+
private calculatePanelSize(iconsRowSize: number, iconRows = 4) {
this.iconsPanelHeight = Math.min(iconRows * this.iconsRowHeight, 10 * this.iconsRowHeight) + 'px';
this.iconsPanelWidth = (iconsRowSize * 36 + (iconsRowSize - 1) * 12 + 6) + 'px';
diff --git a/ui-ngx/src/app/shared/components/phone-input.component.scss b/ui-ngx/src/app/shared/components/phone-input.component.scss
index eb8d27646f..1ba5eabf3a 100644
--- a/ui-ngx/src/app/shared/components/phone-input.component.scss
+++ b/ui-ngx/src/app/shared/components/phone-input.component.scss
@@ -24,7 +24,6 @@
.phone-input-container {
display: flex;
- align-items: center;
.phone-input {
width: 100%;
@@ -32,10 +31,11 @@
}
.flags-select-container {
- display: inline-block;
+ display: flex;
+ align-items: center;
position: relative;
width: 50px;
- height: 100%;
+ height: 56px;
margin-right: 5px;
}
diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.html b/ui-ngx/src/app/shared/components/time/timewindow.component.html
index e445e99c98..a53ea656fc 100644
--- a/ui-ngx/src/app/shared/components/time/timewindow.component.html
+++ b/ui-ngx/src/app/shared/components/time/timewindow.component.html
@@ -41,26 +41,16 @@
-
-
+ [class]="{'no-padding': noPadding}"
+ matTooltip="{{ 'timewindow.edit' | translate }}"
+ [matTooltipPosition]="tooltipPosition"
+ [style]="timewindowComponentStyle"
+ (click)="toggleTimewindow($event)">
+ {{ computedTimewindowStyle.icon }}
+
{{innerValue?.displayValue}} | {{innerValue?.displayTimezoneAbbr}}
-
-
+
+ {{ computedTimewindowStyle.icon }}
diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.scss b/ui-ngx/src/app/shared/components/time/timewindow.component.scss
index af3feec6eb..a00fe5bdc1 100644
--- a/ui-ngx/src/app/shared/components/time/timewindow.component.scss
+++ b/ui-ngx/src/app/shared/components/time/timewindow.component.scss
@@ -24,15 +24,24 @@
max-width: 100%;
}
section.tb-timewindow {
- min-height: 32px;
padding: 0 8px;
+ &.no-padding {
+ padding: 0;
+ }
+ line-height: 32px;
+ pointer-events: all;
+ cursor: pointer;
+ display: flex;
+ flex-direction: row;
+ place-content: center flex-start;
+ align-items: center;
+ gap: 4px;
+ width: 100%;
- span {
+ .tb-timewindow-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- pointer-events: all;
- cursor: pointer;
}
.timezone-abbr {
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 ff39e54fc7..ba8b4ba114 100644
--- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts
+++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts
@@ -20,7 +20,7 @@ import {
ElementRef,
forwardRef, HostBinding,
Injector,
- Input,
+ Input, OnChanges, OnInit, SimpleChanges,
StaticProvider,
ViewContainerRef
} from '@angular/core';
@@ -50,6 +50,12 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { coerceBoolean } from '@shared/decorators/coercion';
+import {
+ ComponentStyle,
+ defaultTimewindowStyle, iconStyle,
+ textStyle,
+ TimewindowStyle
+} from '@shared/models/widget-settings.models';
// @dynamic
@Component({
@@ -64,7 +70,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
}
]
})
-export class TimewindowComponent implements ControlValueAccessor {
+export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChanges {
historyOnlyValue = false;
@@ -88,6 +94,14 @@ export class TimewindowComponent implements ControlValueAccessor {
@coerceBoolean()
noMargin = false;
+ @Input()
+ @coerceBoolean()
+ noPadding = false;
+
+ @Input()
+ @coerceBoolean()
+ disablePanel = false;
+
@Input()
@coerceBoolean()
forAllTimeEnabled = false;
@@ -145,10 +159,10 @@ export class TimewindowComponent implements ControlValueAccessor {
}
@Input()
- direction: 'left' | 'right' = 'left';
+ tooltipPosition: TooltipPosition = 'above';
@Input()
- tooltipPosition: TooltipPosition = 'above';
+ timewindowStyle: TimewindowStyle;
@Input()
@coerceBoolean()
@@ -158,6 +172,10 @@ export class TimewindowComponent implements ControlValueAccessor {
timewindowDisabled: boolean;
+ computedTimewindowStyle: TimewindowStyle;
+ timewindowComponentStyle: ComponentStyle;
+ timewindowIconStyle: ComponentStyle;
+
private propagateChange = (_: any) => {};
constructor(private overlay: Overlay,
@@ -170,10 +188,28 @@ export class TimewindowComponent implements ControlValueAccessor {
public viewContainerRef: ViewContainerRef) {
}
+ ngOnInit() {
+ this.updateTimewindowStyle();
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ for (const propName of Object.keys(changes)) {
+ const change = changes[propName];
+ if (!change.firstChange && change.currentValue !== change.previousValue) {
+ if (propName === 'timewindowStyle') {
+ this.updateTimewindowStyle();
+ }
+ }
+ }
+ }
+
toggleTimewindow($event: Event) {
if ($event) {
$event.stopPropagation();
}
+ if (this.disablePanel) {
+ return;
+ }
const config = new OverlayConfig({
panelClass: 'tb-timewindow-panel',
backdropClass: 'cdk-overlay-transparent-backdrop',
@@ -226,6 +262,17 @@ export class TimewindowComponent implements ControlValueAccessor {
this.cd.detectChanges();
}
+ private updateTimewindowStyle() {
+ if (!this.asButton) {
+ this.computedTimewindowStyle = {...defaultTimewindowStyle, ...(this.timewindowStyle || {})};
+ this.timewindowComponentStyle = textStyle(this.computedTimewindowStyle.font);
+ if (this.computedTimewindowStyle.color) {
+ this.timewindowComponentStyle.color = this.computedTimewindowStyle.color;
+ }
+ this.timewindowIconStyle = this.computedTimewindowStyle.iconSize ? iconStyle(this.computedTimewindowStyle.iconSize) : {};
+ }
+ }
+
private onHistoryOnlyChanged(): boolean {
if (this.historyOnlyValue && this.innerValue && this.innerValue.selectedTab !== TimewindowType.HISTORY) {
this.innerValue.selectedTab = TimewindowType.HISTORY;
diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts
index f7c1b6746e..18782cdd93 100644
--- a/ui-ngx/src/app/shared/models/widget-settings.models.ts
+++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts
@@ -60,6 +60,7 @@ export interface Font {
family: string;
weight: fontWeight;
style: fontStyle;
+ lineHeight: string;
}
export enum ColorType {
@@ -89,6 +90,22 @@ export interface ColorSettings {
colorFunction?: string;
}
+export interface TimewindowStyle {
+ showIcon: boolean;
+ icon: string;
+ iconSize: string;
+ iconPosition: 'left' | 'right';
+ font?: Font;
+ color?: string;
+}
+
+export const defaultTimewindowStyle: TimewindowStyle = {
+ showIcon: true,
+ icon: 'query_builder',
+ iconSize: '24px',
+ iconPosition: 'left'
+};
+
export const constantColor = (color: string): ColorSettings => ({
type: ColorType.constant,
color,
@@ -298,19 +315,19 @@ export interface BackgroundSettings {
overlay: OverlaySettings;
}
-export const iconStyle = (size: number, sizeUnit: cssUnit): ComponentStyle => {
- const iconSize = size + sizeUnit;
+export const iconStyle = (size: number | string, sizeUnit: cssUnit = 'px'): ComponentStyle => {
+ const iconSize = typeof size === 'number' ? size + sizeUnit : size;
return {
width: iconSize,
+ minWidth: iconSize,
height: iconSize,
fontSize: iconSize,
lineHeight: iconSize
};
};
-export const textStyle = (font?: Font, lineHeight = '1.5', letterSpacing = '0.25px'): ComponentStyle => {
+export const textStyle = (font?: Font, letterSpacing = 'normal'): ComponentStyle => {
const style: ComponentStyle = {
- lineHeight,
letterSpacing
};
if (font?.style) {
@@ -319,6 +336,9 @@ export const textStyle = (font?: Font, lineHeight = '1.5', letterSpacing = '0.25
if (font?.weight) {
style.fontWeight = font.weight;
}
+ if (font?.lineHeight) {
+ style.lineHeight = font.lineHeight;
+ }
if (font?.size) {
style.fontSize = (font.size + (font.sizeUnit || 'px'));
}
diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts
index c3b23048d1..3a566d8b9c 100644
--- a/ui-ngx/src/app/shared/models/widget.models.ts
+++ b/ui-ngx/src/app/shared/models/widget.models.ts
@@ -40,7 +40,7 @@ import { Dashboard } from '@shared/models/dashboard.models';
import { IAliasController } from '@core/api/widget-api.models';
import { isEmptyStr } from '@core/utils';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
-import { ComponentStyle, Font } from '@shared/models/widget-settings.models';
+import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models';
export enum widgetType {
timeseries = 'timeseries',
@@ -183,7 +183,7 @@ export interface WidgetTypeParameters {
processNoDataByWidget?: boolean;
previewWidth?: string;
previewHeight?: string;
- absoluteHeader?: boolean;
+ embedTitlePanel?: boolean;
}
export interface WidgetControllerDescriptor {
@@ -633,6 +633,7 @@ export interface WidgetConfig {
useDashboardTimewindow?: boolean;
displayTimewindow?: boolean;
timewindow?: Timewindow;
+ timewindowStyle?: TimewindowStyle;
desktopHide?: boolean;
mobileHide?: boolean;
mobileHeight?: number;
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 026ebf857e..f90a34fe8e 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -725,7 +725,8 @@
"no-attributes-text": "No attributes found",
"no-telemetry-text": "No telemetry found",
"copy-key": "Copy key",
- "copy-value": "Copy value"
+ "copy-value": "Copy value",
+ "add-telemetry": "Add telemetry"
},
"api-usage": {
"api-features": "API features",
@@ -3902,7 +3903,15 @@
"interval": "Interval",
"just-now": "Just now",
"just-now-lower": "just now",
- "ago": "ago"
+ "ago": "ago",
+ "style": "Timewindow style",
+ "icon": "Icon",
+ "icon-position": "Icon position",
+ "icon-position-left": "Left",
+ "icon-position-right": "Right",
+ "font": "Font",
+ "color": "Color",
+ "preview": "Preview"
},
"unit": {
"millimeter": "Millimeter",
@@ -5799,7 +5808,8 @@
"font-weight-lighter": "Lighter",
"color": "Color",
"shadow-color": "Shadow color",
- "preview": "Preview"
+ "preview": "Preview",
+ "line-height": "Line height"
},
"home": {
"no-data-available": "No data available"