diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index 3048179241..cea09a91c1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -225,6 +225,7 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn this.keyRowFormGroup.get('units').patchValue(this.modelValue.units, {emitEvent: false}); this.keyRowFormGroup.get('decimals').patchValue(this.modelValue.decimals, {emitEvent: false}); this.updateModel(); + this.cd.markForCheck(); } }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html index 8f0b79cca6..99508e5139 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html @@ -29,6 +29,11 @@
widget-config.appearance
+
+ + {{ 'widgets.aggregated-value-card.auto-scale' | translate }} + +
{{ 'widget-config.title' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts index f8e69c9d7a..90a020cac8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts @@ -119,6 +119,7 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo timewindowConfig: [getTimewindowConfig(configData.config), []], datasources: [configData.config.datasources, []], + autoScale: [settings.autoScale, []], showTitle: [configData.config.showTitle, []], title: [configData.config.title, []], titleFont: [configData.config.titleFont, []], @@ -172,6 +173,7 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.widgetConfig.config.settings.autoScale = config.autoScale; this.widgetConfig.config.settings.showSubtitle = config.showSubtitle; this.widgetConfig.config.settings.subtitle = config.subtitle; this.widgetConfig.config.settings.subtitleFont = config.subtitleFont; 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 a036ab2b1c..4550263a76 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 @@ -40,6 +40,11 @@ {{ valueCardLayoutTranslationMap.get(layout) | translate }} +
+ + {{ 'widgets.value-card.auto-scale' | translate }} + +
{{ 'widgets.value-card.label' | translate }} 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 dacb2dfa11..629fce4081 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 @@ -116,6 +116,7 @@ export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent { timewindowConfig: [getTimewindowConfig(configData.config), []], datasources: [configData.config.datasources, []], layout: [settings.layout, []], + autoScale: [settings.autoScale, []], showLabel: [settings.showLabel, []], label: [getLabel(configData.config.datasources), []], @@ -154,6 +155,7 @@ export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; this.widgetConfig.config.settings.layout = config.layout; + this.widgetConfig.config.settings.autoScale = config.autoScale; this.widgetConfig.config.settings.showLabel = config.showLabel; setLabel(config.label, this.widgetConfig.config.datasources); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.html index edd0cda3b2..8133acedfb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.html @@ -21,7 +21,9 @@
- + + +
@@ -31,8 +33,8 @@ [style]="subtitleStyle" [style.color]="subtitleColor">{{ subtitle$ | async }}
-
-
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss index 6dee828c36..3f7d33d57b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss @@ -21,7 +21,7 @@ display: flex; flex-direction: column; align-items: stretch; - gap: 8px; + gap: 2px; padding: 20px 24px 24px; > div:not(.tb-value-card-overlay) { z-index: 1; @@ -45,10 +45,17 @@ min-height: 0; overflow: hidden; } + .tb-aggregated-value-card-values { + padding: 8px 0; + position: relative; + display: flex; + align-items: center; + justify-content: center; + } .tb-aggregated-value-card-values-container { width: 100%; height: 100%; - padding: 8px 0; + position: relative; display: grid; grid-template-columns: minmax(0, 1fr) fit-content(100%) minmax(0, 1fr); .tb-aggregated-value-card-values-section { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts index 991dbe80f9..5380874811 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -20,7 +20,9 @@ import { Component, ElementRef, Input, + OnDestroy, OnInit, + Renderer2, TemplateRef, ViewChild } from '@angular/core'; @@ -43,22 +45,31 @@ import { overlayStyle, textStyle } from '@shared/models/widget-settings.models'; -import { DatePipe } from '@angular/common'; import { TbFlot } from '@home/components/widget/lib/flot-widget'; import { TbFlotKeySettings, TbFlotSettings } from '@home/components/widget/lib/flot-widget.models'; import { DataKey } from '@shared/models/widget.models'; import { formatNumberValue, formatValue, isDefined } from '@core/utils'; import { map } from 'rxjs/operators'; +import { ResizeObserver } from '@juggle/resize-observer'; + +const valuesLayoutHeight = 66; +const valuesLayoutVerticalPadding = 16; @Component({ selector: 'tb-aggregated-value-card-widget', templateUrl: './aggregated-value-card-widget.component.html', styleUrls: ['./aggregated-value-card-widget.component.scss'] }) -export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit { +export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild('chartElement', {static: false}) chartElement: ElementRef; + @ViewChild('valueCardValues', {static: false}) + valueCardValues: ElementRef; + + @ViewChild('valueCardValueContainer', {static: false}) + valueCardValueContainer: ElementRef; + aggregatedValueCardKeyPosition = AggregatedValueCardKeyPosition; settings: AggregatedValueCardWidgetSettings; @@ -96,7 +107,9 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit tickMin$: Observable; tickMax$: Observable; - constructor(private date: DatePipe, + private panelResize$: ResizeObserver; + + constructor(private renderer: Renderer2, private cd: ChangeDetectorRef) { } @@ -174,6 +187,22 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit (this.flotDataKey?.units || this.ctx.units)) )); } + if (this.settings.autoScale && this.showValues) { + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'height', valuesLayoutHeight + 'px'); + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'position', 'absolute'); + this.panelResize$ = new ResizeObserver(() => { + this.onValueCardValuesResize(); + }); + this.panelResize$.observe(this.valueCardValues.nativeElement); + this.onValueCardValuesResize(); + } + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } } public onInit() { @@ -249,4 +278,17 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit } } + private onValueCardValuesResize() { + const panelWidth = this.valueCardValues.nativeElement.getBoundingClientRect().width; + const panelHeight = this.valueCardValues.nativeElement.getBoundingClientRect().height - valuesLayoutVerticalPadding; + const targetWidth = panelWidth; + const minAspect = 0.25; + const aspect = Math.min(panelHeight / targetWidth, minAspect); + const targetHeight = targetWidth * aspect; + const scale = targetHeight / valuesLayoutHeight; + const width = targetWidth / scale; + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'width', width + 'px'); + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'transform', `scale(${scale})`); + } + } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts index c1e4fafeff..c27fe9df96 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts @@ -32,6 +32,7 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { AggregationType } from '@shared/models/time/time.models'; export interface AggregatedValueCardWidgetSettings { + autoScale: boolean; showSubtitle: boolean; subtitle: string; subtitleFont: Font; @@ -112,6 +113,7 @@ export const getTsValueByLatestDataKey = (latestData: Array, dat }; export const aggregatedValueCardDefaultSettings: AggregatedValueCardWidgetSettings = { + autoScale: true, showSubtitle: true, subtitle: '${entityName}', subtitleFont: { 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 377e0a31f8..16ad55078e 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 @@ -15,12 +15,13 @@ limitations under the License. --> -
+
- +
+ @@ -49,6 +50,7 @@ +
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 39293a9fb3..1ea9825751 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 @@ -19,24 +19,15 @@ height: 100%; position: relative; display: flex; - flex-direction: column; align-items: center; justify-content: center; - gap: 16px; padding: 24px; + &.horizontal, &.horizontal_reversed { + padding: 0 24px; + } > div:not(.tb-value-card-overlay), > tb-icon { z-index: 1; } - &.square { - justify-content: space-evenly; - gap: 0; - } - &.horizontal { - flex-direction: row; - } - &.horizontal_reversed { - flex-direction: row-reverse; - } .tb-value-card-overlay { position: absolute; top: 12px; @@ -51,27 +42,54 @@ right: 12px; z-index: 2; } - .tb-value-card-icon-row { + .tb-value-card-content { + width: 100%; + height: 100%; + position: relative; display: flex; - flex-direction: row; + flex-direction: column; align-items: center; - gap: 8px; - } - &.horizontal_reversed { - .tb-value-card-icon-row { + justify-content: center; + gap: 16px; + &.square { + justify-content: space-evenly; + gap: 0; + } + + &.horizontal { + flex-direction: row; + } + + &.horizontal_reversed { flex-direction: row-reverse; } + + .tb-value-card-icon-row { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + } + + &.horizontal_reversed { + .tb-value-card-icon-row { + flex-direction: row-reverse; + } + + .tb-value-card-label-row { + align-items: flex-end; + } + } + .tb-value-card-label-row { - align-items: flex-end; + display: flex; + flex-direction: column; + justify-content: center; + } + + .tb-value-card-value { + white-space: nowrap; } - } - .tb-value-card-label-row { - display: flex; - flex-direction: column; - justify-content: center; - } - .tb-value-card-value { - white-space: nowrap; } } } 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 48d432bcb7..d723b9b472 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,14 +14,25 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, OnInit, TemplateRef } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild +} from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; import { formatValue, isDefinedAndNotNull } from '@core/utils'; -import { DatePipe } from '@angular/common'; import { backgroundStyle, ColorProcessor, - ComponentStyle, DateFormatProcessor, + ComponentStyle, + DateFormatProcessor, getDataKey, getLabel, getSingleTsValue, @@ -32,13 +43,24 @@ import { import { valueCardDefaultSettings, ValueCardLayout, ValueCardWidgetSettings } from './value-card-widget.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; import { Observable } from 'rxjs'; +import { ResizeObserver } from '@juggle/resize-observer'; + +const squareLayoutSize = 160; +const squareLayoutPadding = 48; +const horizontalLayoutHeight = 80; @Component({ selector: 'tb-value-card-widget', templateUrl: './value-card-widget.component.html', styleUrls: ['./value-card-widget.component.scss'] }) -export class ValueCardWidgetComponent implements OnInit { +export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('valueCardPanel', {static: false}) + valueCardPanel: ElementRef; + + @ViewChild('valueCardContent', {static: false}) + valueCardContent: ElementRef; settings: ValueCardWidgetSettings; @@ -73,11 +95,13 @@ export class ValueCardWidgetComponent implements OnInit { backgroundStyle: ComponentStyle = {}; overlayStyle: ComponentStyle = {}; + private panelResize$: ResizeObserver; + private horizontal = false; private decimals = 0; private units = ''; - constructor(private date: DatePipe, + constructor(private renderer: Renderer2, private widgetComponent: WidgetComponent, private cd: ChangeDetectorRef) { } @@ -122,6 +146,29 @@ export class ValueCardWidgetComponent implements OnInit { this.overlayStyle = overlayStyle(this.settings.background.overlay); } + public ngAfterViewInit() { + if (this.settings.autoScale) { + if (!this.horizontal) { + this.renderer.setStyle(this.valueCardContent.nativeElement, 'width', squareLayoutSize + 'px'); + } + const height = this.horizontal ? horizontalLayoutHeight : squareLayoutSize; + this.renderer.setStyle(this.valueCardContent.nativeElement, 'height', height + 'px'); + this.renderer.setStyle(this.valueCardContent.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.valueCardContent.nativeElement, 'position', 'absolute'); + this.panelResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.panelResize$.observe(this.valueCardPanel.nativeElement); + this.onResize(); + } + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } + } + public onInit() { const borderRadius = this.ctx.$widgetElement.css('borderRadius'); this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; @@ -146,4 +193,22 @@ export class ValueCardWidgetComponent implements OnInit { this.dateColor.update(value); this.cd.detectChanges(); } + + private onResize() { + const panelWidth = this.valueCardPanel.nativeElement.getBoundingClientRect().width - squareLayoutPadding; + const panelHeight = this.valueCardPanel.nativeElement.getBoundingClientRect().height - (this.horizontal ? 0 : squareLayoutPadding); + let scale: number; + if (!this.horizontal) { + const size = Math.min(panelWidth, panelHeight); + scale = size / squareLayoutSize; + } else { + const targetWidth = panelWidth; + const aspect = Math.min(panelHeight / targetWidth, 0.25); + const targetHeight = targetWidth * aspect; + scale = targetHeight / horizontalLayoutHeight; + const width = targetWidth / scale; + this.renderer.setStyle(this.valueCardContent.nativeElement, 'width', width + 'px'); + } + this.renderer.setStyle(this.valueCardContent.nativeElement, 'transform', `scale(${scale})`); + } } 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 aa442bd199..481c50d254 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 @@ -64,6 +64,7 @@ export const valueCardLayoutImages = new Map( export interface ValueCardWidgetSettings { layout: ValueCardLayout; + autoScale: boolean; showLabel: boolean; labelFont: Font; labelColor: ColorSettings; @@ -83,6 +84,7 @@ export interface ValueCardWidgetSettings { export const valueCardDefaultSettings = (horizontal: boolean): ValueCardWidgetSettings => ({ layout: horizontal ? ValueCardLayout.horizontal : ValueCardLayout.square, + autoScale: true, showLabel: true, labelFont: { family: 'Roboto', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html index a3d70ed08b..a8c8ccf675 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html @@ -15,23 +15,25 @@ limitations under the License. --> -
-
- -
-
-
-
+
+
+
+ +
+
+
+
+
+ {{ icon }} +
+
+
{{ label }}
+
+
{{ valueText }}
- {{ icon }} -
-
-
{{ label }}
-
-
{{ valueText }}
+ chevron_right
- chevron_right
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss index 8deb211897..e2d526a3c7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss @@ -17,55 +17,71 @@ .tb-count-panel { width: 100%; height: 100%; + position: relative; display: flex; - flex-direction: row; align-items: center; - gap: 12px; + justify-content: center; padding: 12px; &.tb-count-pointer { cursor: pointer; } - .tb-count-panel-column { - flex: 1; - display: flex; - flex-direction: column; - gap: 12px; - } - .tb-count-panel-row { - flex: 1; + .tb-count-panel-content { + width: 100%; + height: 100%; + position: relative; display: flex; flex-direction: row; align-items: center; gap: 12px; - } - .tb-count-icon-panel { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - .mat-icon { - z-index: 1; + .tb-count-panel-column { + flex: 1; + display: flex; + flex-direction: column; + gap: 12px; + overflow: hidden; } - } - .tb-count-icon-background-panel { - position: absolute; - inset: 0; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - .tb-count-label-value-panel { - flex: 1; - display: flex; - flex-direction: column; - place-content: flex-start space-around; - align-items: flex-start; - gap: 0; - &.tb-count-layout-row { + .tb-count-panel-row { + flex: 1; + display: flex; flex-direction: row; align-items: center; + gap: 12px; + } + .tb-count-icon-panel { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .mat-icon { + z-index: 1; + } + } + .tb-count-icon-background-panel { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .tb-count-label-value-panel { + flex: 1; + display: flex; + flex-direction: column; + place-content: flex-start space-around; + align-items: flex-start; + gap: 0; + overflow: hidden; + &.tb-count-layout-row { + flex-direction: row; + align-items: center; + } + .tb-count-label { + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + } } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts index 1f617958e8..0f3c922e1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts @@ -14,7 +14,18 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, OnInit, TemplateRef } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild +} from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; import { formatValue } from '@core/utils'; import { @@ -24,20 +35,30 @@ import { iconStyle, textStyle } from '@shared/models/widget-settings.models'; -import { WidgetComponent } from '@home/components/widget/widget.component'; import { CountCardLayout, countDefaultSettings, CountWidgetSettings } from '@home/components/widget/lib/count/count-widget.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; + +const layoutHeight = 36; +const layoutHeightWithTitle = 60; +const layoutPadding = 24; @Component({ selector: 'tb-count-widget', templateUrl: './count-widget.component.html', styleUrls: ['./count-widget.component.scss'] }) -export class CountWidgetComponent implements OnInit { +export class CountWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('countPanel', {static: false}) + countPanel: ElementRef; + + @ViewChild('countPanelContent', {static: false}) + countPanelContent: ElementRef; settings: CountWidgetSettings; @@ -79,7 +100,10 @@ export class CountWidgetComponent implements OnInit { hasCardClickAction = false; - constructor(private widgetComponent: WidgetComponent, + private panelResize$: ResizeObserver; + private hasTitle = false; + + constructor(private renderer: Renderer2, private cd: ChangeDetectorRef) { } @@ -120,6 +144,27 @@ export class CountWidgetComponent implements OnInit { this.chevronStyle.color = this.settings.chevronColor; this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + this.hasTitle = this.ctx.widgetConfig.showTitle; + } + + public ngAfterViewInit() { + if (this.settings.autoScale) { + const height = this.hasTitle ? layoutHeightWithTitle : layoutHeight; + this.renderer.setStyle(this.countPanelContent.nativeElement, 'height', height + 'px'); + this.renderer.setStyle(this.countPanelContent.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.countPanelContent.nativeElement, 'position', 'absolute'); + this.panelResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.panelResize$.observe(this.countPanel.nativeElement); + this.onResize(); + } + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } } public onInit() { @@ -144,4 +189,24 @@ export class CountWidgetComponent implements OnInit { public cardClick($event: Event) { this.ctx.actionsApi.cardClick($event); } + + private onResize() { + const panelWidth = this.countPanel.nativeElement.getBoundingClientRect().width - layoutPadding; + const panelHeight = this.countPanel.nativeElement.getBoundingClientRect().height - layoutPadding; + const targetWidth = panelWidth; + let minAspect = 0.25; + if (this.settings.showChevron) { + minAspect -= 0.05; + } + if (this.hasTitle) { + minAspect += 0.15; + } + const aspect = Math.min(panelHeight / targetWidth, minAspect); + const targetHeight = targetWidth * aspect; + const height = this.hasTitle ? layoutHeightWithTitle : layoutHeight; + const scale = targetHeight / height; + const width = targetWidth / scale; + this.renderer.setStyle(this.countPanelContent.nativeElement, 'width', width + 'px'); + this.renderer.setStyle(this.countPanelContent.nativeElement, 'transform', `scale(${scale})`); + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts index 4a1b307f4b..f33c1f0523 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts @@ -53,6 +53,7 @@ export const entityCountCardLayoutImages = new Map( export interface CountWidgetSettings { layout: CountCardLayout; + autoScale: boolean; showLabel: boolean; label: string; labelFont: Font; @@ -76,6 +77,7 @@ export interface CountWidgetSettings { export const countDefaultSettings = (alarmElseEntity: boolean): CountWidgetSettings => ({ layout: CountCardLayout.column, + autoScale: true, showLabel: true, label: alarmElseEntity ? 'Total' : 'Devices', labelFont: { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html index c4af456487..2e196f0598 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html @@ -18,6 +18,11 @@
widgets.aggregated-value-card.aggregated-value-card-style
+
+ + {{ 'widgets.aggregated-value-card.auto-scale' | translate }} + +
{{ 'widgets.aggregated-value-card.subtitle' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts index bb2fda0b50..d11ac6c8c8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts @@ -50,6 +50,7 @@ export class AggregatedValueCardWidgetSettingsComponent extends WidgetSettingsCo protected onSettingsSet(settings: WidgetSettings) { this.aggregatedValueCardWidgetSettingsForm = this.fb.group({ + autoScale: [settings.autoScale, []], showSubtitle: [settings.showSubtitle, []], subtitle: [settings.subtitle, []], subtitleFont: [settings.subtitleFont, []], 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 f0fd9cdb16..fe543dc330 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 @@ -28,6 +28,11 @@ {{ valueCardLayoutTranslationMap.get(layout) | translate }} +
+ + {{ 'widgets.value-card.auto-scale' | translate }} + +
{{ 'widgets.value-card.label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts index ad50dde8d2..4f4b3b6eec 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts @@ -90,6 +90,7 @@ export class ValueCardWidgetSettingsComponent extends WidgetSettingsComponent { protected onSettingsSet(settings: WidgetSettings) { this.valueCardWidgetSettingsForm = this.fb.group({ layout: [settings.layout, []], + autoScale: [settings.autoScale, []], showLabel: [settings.showLabel, []], labelFont: [settings.labelFont, []], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html index e373acb8a5..e33d9ee86e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html @@ -26,6 +26,11 @@ {{ countCardLayoutTranslationMap.get(layout) | translate }} +
+ + {{ 'widgets.count.auto-scale' | translate }} + +
{{ 'widgets.count.label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts index 2a37a46d5c..02e2ad4dc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts @@ -78,6 +78,8 @@ export class CountWidgetSettingsComponent extends PageComponent implements OnIni this.countCardLayoutImageMap = this.alarmElseEntity ? alarmCountCardLayoutImages : entityCountCardLayoutImages; this.countWidgetConfigForm = this.fb.group({ layout: [null, []], + autoScale: [null, []], + showLabel: [null, []], label: [null, []], labelFont: [null, []], 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 a56ee4e456..40abb989c1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6039,7 +6039,8 @@ "icon": "Icon", "value": "Value", "date": "Date", - "value-card-style": "Value card style" + "value-card-style": "Value card style", + "auto-scale": "Auto scale" }, "aggregated-value-card": { "subtitle": "Subtitle", @@ -6060,7 +6061,8 @@ "remove-value": "Remove value", "no-values": "No values configured", "aggregation": "Aggregation", - "aggregated-value-card-style": "Aggregated value card style" + "aggregated-value-card-style": "Aggregated value card style", + "auto-scale": "Auto scale" }, "alarm-count": { "alarm-count-card-style": "Alarm count card style" @@ -6076,7 +6078,8 @@ "icon": "Icon", "icon-background": "Icon background", "value": "Value", - "chevron": "Chevron" + "chevron": "Chevron", + "auto-scale": "Auto scale" }, "table": { "common-table-settings": "Common Table Settings",