From d2beb455971f475d5789de923665bdb3c8cbd097 Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Wed, 3 Jun 2026 17:02:39 +0200 Subject: [PATCH 1/8] Fix "No data" overlay not hiding when widget data becomes available after empty state --- .../src/app/modules/home/components/widget/widget.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 fc7ad9f64b..058a178825 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 @@ -683,6 +683,7 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, } private reInitImpl() { + this.displayNoData = false; this.onDestroy(); if (!this.typeParameters.useCustomDatasources) { this.createDefaultSubscription().subscribe({ @@ -726,7 +727,6 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds); } if (subscriptionChanged && !this.typeParameters.useCustomDatasources) { - this.displayNoData = false; this.reInit(); } } @@ -740,7 +740,6 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, subscriptionChanged = subscriptionChanged || subscription.onFiltersChanged(filterIds); } if (subscriptionChanged && !this.typeParameters.useCustomDatasources) { - this.displayNoData = false; this.reInit(); } } From 78cfbf4b32e9ecf0660ac217c75dd8f4c6fb5953 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 19 Jun 2026 18:27:30 +0300 Subject: [PATCH 2/8] fix(iot-hub): mobile detail-dialog stacks preview+description, hero shrinks on lt-sm, device install URL falls back to first created device Detail dialog non-CF/RC layout stacks the preview and description on lt-md instead of overflowing horizontally. Hero cluster moves up to 15% and title drops to 28px on the smallest viewport. getInstalledItemUrl for DEVICE descriptors now falls back to the first DEVICE entry in createdEntityIds when dashboardId is missing, so single-device packages still resolve to an entity URL. --- .../iot-hub-item-detail-dialog.component.html | 2 +- .../home/pages/iot-hub/iot-hub-home.component.scss | 8 ++++++++ .../models/iot-hub/iot-hub-installed-item.models.ts | 12 ++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.html index e42f1521aa..30d9322dd9 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.html @@ -96,7 +96,7 @@ } @else { -
+
@if (item.type === ItemType.SOLUTION_TEMPLATE && carouselImages.length > 1) { diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss index f0768bf330..c433bad1a9 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.scss @@ -757,6 +757,14 @@ padding: 140px 16px 40px; } + .tb-iot-hub-hero-cluster { + top: 15%; + } + + .tb-iot-hub-hero-title { + font-size: 28px; + } + .tb-iot-hub-categories { grid-template-columns: 1fr; } diff --git a/ui-ngx/src/app/shared/models/iot-hub/iot-hub-installed-item.models.ts b/ui-ngx/src/app/shared/models/iot-hub/iot-hub-installed-item.models.ts index b2320453bb..b2bb0e7ad9 100644 --- a/ui-ngx/src/app/shared/models/iot-hub/iot-hub-installed-item.models.ts +++ b/ui-ngx/src/app/shared/models/iot-hub/iot-hub-installed-item.models.ts @@ -140,8 +140,16 @@ export const getInstalledItemUrl = (descriptor?: IotHubInstalledItemDescriptor): let query: string | null = null; switch (descriptor.type) { case 'DEVICE': - entityId = descriptor.dashboardId?.id; - entityType = EntityType.DASHBOARD; + if (descriptor.dashboardId) { + entityId = descriptor.dashboardId?.id; + entityType = EntityType.DASHBOARD; + } else if (descriptor.createdEntityIds) { + const found = descriptor.createdEntityIds.find(id => id.entityType === EntityType.DEVICE); + if (found) { + entityId = found.id; + entityType = EntityType.DEVICE; + } + } break; case 'WIDGET': entityId = descriptor.widgetTypeId?.id; From f5a1052a480f612e98fe6cde204e02e9fc1329c2 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 19 Jun 2026 20:05:17 +0300 Subject: [PATCH 3/8] fix(iot-hub): tighten delete/update result handling and switch button spinners/icons to matButtonIcon IotHubActionsService.deleteItem now returns true/false (no cancel-as-EMPTY) and updateItem returns string|boolean; callers no longer early-return on a null installed item and instead let the service produce a false, so the subscriber consistently fires. Installed-items table shows a translucent loading overlay during update/delete via isLoading + relative-positioned container. Replace mr-2 inline-block align-middle hacks on dialog button spinners with matButtonIcon, and use matButtonIcon for the check-for-updates button icon/spinner too. --- .../iot-hub/iot-hub-actions.service.ts | 21 +++++++------ .../iot-hub/iot-hub-browse.component.ts | 10 +++--- .../iot-hub-install-dialog.component.html | 10 +++--- ...t-hub-installed-items-table.component.html | 17 +++++----- ...t-hub-installed-items-table.component.scss | 1 + ...iot-hub-installed-items-table.component.ts | 10 ++++-- .../iot-hub-item-detail-dialog.component.ts | 4 +-- .../iot-hub/iot-hub-search.component.ts | 8 ++--- .../iot-hub-update-dialog.component.html | 2 +- .../iot-hub-update-dialog.component.ts | 4 +-- .../pages/iot-hub/iot-hub-home.component.ts | 31 ++++++++++--------- .../iot-hub-installed-items.component.html | 4 +-- 12 files changed, 67 insertions(+), 55 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts index bae080a464..dd0b89598f 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Observable, of, EMPTY } from 'rxjs'; -import { filter, mergeMap } from 'rxjs/operators'; +import { filter, map, mergeMap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '@core/services/dialog.service'; import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models'; @@ -94,11 +94,11 @@ export class IotHubActionsService { }).afterClosed(); } - updateItem(installedItem: IotHubInstalledItem, version: string, versionId: string): Observable { + updateItem(installedItem: IotHubInstalledItem, version: string, versionId: string): Observable { if (!installedItem) { - return EMPTY; + return of(false); } - return this.dialog.open(TbIotHubUpdateDialogComponent, { + return this.dialog.open(TbIotHubUpdateDialogComponent, { panelClass: ['tb-dialog'], disableClose: true, autoFocus: false, @@ -108,7 +108,7 @@ export class IotHubActionsService { itemType: installedItem.itemType as ItemType, version, versionId - } as IotHubUpdateDialogData + } }).afterClosed(); } @@ -116,15 +116,16 @@ export class IotHubActionsService { if (!installedItem) { return of(false); } - return this.dialog.open(TbIotHubDeleteDialogComponent, { + return this.dialog.open(TbIotHubDeleteDialogComponent, { panelClass: ['tb-dialog'], disableClose: true, autoFocus: false, - data: { itemName: installedItem.itemName, itemType: installedItem.itemType } as IotHubDeleteDialogData + data: { itemName: installedItem.itemName, itemType: installedItem.itemType } }).afterClosed().pipe( - filter(confirmed => !!confirmed), - mergeMap(() => this.iotHubApiService.deleteInstalledItem(installedItem.id.id)), - mergeMap(() => of(true)) + mergeMap((confirmed) => + confirmed + ? this.iotHubApiService.deleteInstalledItem(installedItem.id.id).pipe(map(() => true)) + : of(false) ) ); } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts index 2efea716bf..5954ebc49f 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts @@ -580,8 +580,7 @@ export class TbIotHubBrowseComponent implements OnInit, AfterViewInit, OnDestroy updateItem(item: MpItemVersionView): void { const installedItem = this.getInstalledItem(item); - if (!installedItem) { return; } - this.iotHubActions.updateItem(installedItem, item.version, item.id as string).subscribe(result => { + this.iotHubActions.updateItem(installedItem, item.version, item.id).subscribe(result => { if (result === 'updated') { this.reloadInstalledItems(); } @@ -590,9 +589,10 @@ export class TbIotHubBrowseComponent implements OnInit, AfterViewInit, OnDestroy deleteInstalledItem(item: MpItemVersionView): void { const installedItem = this.getInstalledItem(item); - if (!installedItem) { return; } - this.iotHubActions.deleteItem(installedItem).subscribe(() => { - this.reloadInstalledItems(); + this.iotHubActions.deleteItem(installedItem).subscribe((deleted) => { + if (deleted) { + this.reloadInstalledItems(); + } }); } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html index bb551a4106..ea3774ac11 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html @@ -157,7 +157,7 @@ @@ -169,7 +169,7 @@ [disabled]="ruleChainInstallForm.invalid || resolvingPlan" (click)="onRuleChainInstall()"> @if (resolvingPlan) { - + } {{ 'iot-hub.install' | translate }} @@ -178,7 +178,7 @@ [disabled]="(activeSelectEntityConfig.required && !selectedEntityId) || resolvingPlan" (click)="onEntitySelectInstall()"> @if (resolvingPlan) { - + } {{ 'iot-hub.install' | translate }} @@ -188,7 +188,7 @@ @@ -204,7 +204,7 @@ @case ('installing') { } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html index a64f9ab73e..11143dc0bf 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.html @@ -135,14 +135,8 @@ - @if (isLoading) { -
- -
- } - @if (!isLoading && dataSource.length === 0) { -
+

{{ 'iot-hub.no-installed-items' | translate }}

{{ 'iot-hub.no-installed-items-text' | translate }}

@@ -166,4 +160,13 @@ (page)="onPageChange($event)" [showFirstLastButtons]="true"> + + @if (isLoading) { +
+
+ +
+
+ } +
diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss index 400bb5778b..a908470ae8 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.scss @@ -25,6 +25,7 @@ // Table + paginator container — border .tb-installed-table-container { + position: relative; border: 1px solid rgba(0, 0, 0, 0.12); } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts index bb5e1ecd7a..281d1d1e0e 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-installed-items-table.component.ts @@ -156,9 +156,12 @@ export class TbIotHubInstalledItemsTableComponent implements OnInit, OnChanges, } deleteItem(item: IotHubInstalledItem): void { - this.iotHubActions.deleteItem(item).subscribe(confirmed => { - if (confirmed) { + this.isLoading = true; + this.iotHubActions.deleteItem(item).subscribe(deleted => { + if (deleted) { this.loadData(); + } else { + this.isLoading = false; } }); } @@ -244,9 +247,12 @@ export class TbIotHubInstalledItemsTableComponent implements OnInit, OnChanges, } updateItem(item: IotHubInstalledItem, publishedInfo: ItemPublishedVersionInfo): void { + this.isLoading = true; this.iotHubActions.updateItem(item, publishedInfo.publishedVersion, publishedInfo.publishedVersionId).subscribe(result => { if (result === 'updated') { this.loadData(); + } else { + this.isLoading = false; } }); } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts index 04d49b7f86..bc72b7a94c 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts @@ -220,8 +220,8 @@ export class TbIotHubItemDetailDialogComponent extends DialogComponent { - if (confirmed) { + this.iotHubActions.deleteItem(this.installedItem).subscribe(deleted => { + if (deleted) { this.dialogRef.close('deleted'); } }); diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-search.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-search.component.ts index 64ff558723..790baf05f7 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-search.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-search.component.ts @@ -247,7 +247,6 @@ export class TbIotHubSearchComponent implements OnInit, OnDestroy { updateItem(item: MpItemVersionView): void { const installedItem = this.getInstalledItem(item); - if (!installedItem) { return; } this.iotHubActions.updateItem(installedItem, item.version, item.id as string).subscribe(result => { if (result === 'updated') { this.reloadInstalledItems(); @@ -257,9 +256,10 @@ export class TbIotHubSearchComponent implements OnInit, OnDestroy { deleteInstalledItem(item: MpItemVersionView): void { const installedItem = this.getInstalledItem(item); - if (!installedItem) { return; } - this.iotHubActions.deleteItem(installedItem).subscribe(() => { - this.reloadInstalledItems(); + this.iotHubActions.deleteItem(installedItem).subscribe((deleted) => { + if (deleted) { + this.reloadInstalledItems(); + } }); } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.html index 78e8fc3b66..48396089ca 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.html @@ -51,7 +51,7 @@ @case ('updating') { } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.ts index 6962073104..67ef8c14f9 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-update-dialog.component.ts @@ -46,7 +46,7 @@ export type UpdateState = 'confirm' | 'updating' | 'success' | 'error'; templateUrl: './iot-hub-update-dialog.component.html', styleUrls: ['./iot-hub-install-dialog.component.scss'] }) -export class TbIotHubUpdateDialogComponent extends DialogComponent { +export class TbIotHubUpdateDialogComponent extends DialogComponent { ItemType = ItemType; @@ -58,7 +58,7 @@ export class TbIotHubUpdateDialogComponent extends DialogComponent, protected router: Router, - protected dialogRef: MatDialogRef, + protected dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: IotHubUpdateDialogData, private dialog: MatDialog, private dialogService: DialogService, diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts index 87f8eee0b2..07c3b701eb 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-home.component.ts @@ -381,7 +381,6 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { updateItem(item: MpItemVersionView): void { const installedItem = this.findInstalledItem(item); - if (!installedItem) { return; } this.iotHubActions.updateItem(installedItem, item.version, item.id as string).subscribe(result => { if (result === 'updated') { this.reloadInstalledItems(item.type); @@ -404,20 +403,22 @@ export class TbIotHubHomeComponent implements OnInit, OnDestroy { deleteInstalledItem(item: MpItemVersionView): void { const installedItem = this.findInstalledItem(item); if (!installedItem) { return; } - this.iotHubActions.deleteItem(installedItem).subscribe(() => { - this.installedItemsCount = Math.max(0, this.installedItemsCount - 1); - if (item.type === ItemType.WIDGET) { - this.installedWidgets = this.installedWidgets.filter(i => i.id.id !== installedItem.id.id); - } else if (item.type === ItemType.SOLUTION_TEMPLATE) { - this.installedSolutionTemplates = this.installedSolutionTemplates.filter(i => i.id.id !== installedItem.id.id); - } else if (item.type === ItemType.DEVICE && this.installedDeviceCounts[item.itemId]) { - this.installedDeviceCounts[item.itemId] = Math.max(0, this.installedDeviceCounts[item.itemId] - 1); - } else if (item.type === ItemType.CALCULATED_FIELD && this.installedCalcFieldCounts[item.itemId]) { - this.installedCalcFieldCounts[item.itemId] = Math.max(0, this.installedCalcFieldCounts[item.itemId] - 1); - } else if (item.type === ItemType.ALARM_RULE && this.installedAlarmRuleCounts[item.itemId]) { - this.installedAlarmRuleCounts[item.itemId] = Math.max(0, this.installedAlarmRuleCounts[item.itemId] - 1); - } else if (item.type === ItemType.RULE_CHAIN && this.installedRuleChainCounts[item.itemId]) { - this.installedRuleChainCounts[item.itemId] = Math.max(0, this.installedRuleChainCounts[item.itemId] - 1); + this.iotHubActions.deleteItem(installedItem).subscribe((deleted) => { + if (deleted) { + this.installedItemsCount = Math.max(0, this.installedItemsCount - 1); + if (item.type === ItemType.WIDGET) { + this.installedWidgets = this.installedWidgets.filter(i => i.id.id !== installedItem.id.id); + } else if (item.type === ItemType.SOLUTION_TEMPLATE) { + this.installedSolutionTemplates = this.installedSolutionTemplates.filter(i => i.id.id !== installedItem.id.id); + } else if (item.type === ItemType.DEVICE && this.installedDeviceCounts[item.itemId]) { + this.installedDeviceCounts[item.itemId] = Math.max(0, this.installedDeviceCounts[item.itemId] - 1); + } else if (item.type === ItemType.CALCULATED_FIELD && this.installedCalcFieldCounts[item.itemId]) { + this.installedCalcFieldCounts[item.itemId] = Math.max(0, this.installedCalcFieldCounts[item.itemId] - 1); + } else if (item.type === ItemType.ALARM_RULE && this.installedAlarmRuleCounts[item.itemId]) { + this.installedAlarmRuleCounts[item.itemId] = Math.max(0, this.installedAlarmRuleCounts[item.itemId] - 1); + } else if (item.type === ItemType.RULE_CHAIN && this.installedRuleChainCounts[item.itemId]) { + this.installedRuleChainCounts[item.itemId] = Math.max(0, this.installedRuleChainCounts[item.itemId] - 1); + } } }); } diff --git a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html index be6793f766..1061eb07a9 100644 --- a/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html +++ b/ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.html @@ -28,9 +28,9 @@ @if (!updatesChecked) { From d3bc589a510b995fbcaa8ae635974ec4915ba84e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 19 Jun 2026 20:10:39 +0300 Subject: [PATCH 4/8] chore(iot-hub): drop now-unused filter import from IotHubActionsService --- .../modules/home/components/iot-hub/iot-hub-actions.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts index dd0b89598f..702eb6b1d9 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Observable, of, EMPTY } from 'rxjs'; -import { filter, map, mergeMap } from 'rxjs/operators'; +import { map, mergeMap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '@core/services/dialog.service'; import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models'; From 2b635c674d82503a9a2bbd8208ec6dfab32d6f3f Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Mon, 22 Jun 2026 11:00:40 +0200 Subject: [PATCH 5/8] Chart Widgets: zoom interaction now syncs the active time window (#15624) * Chart Widgets: zoom interaction now syncs the active time window * Fix for comparison mode * Minor fixes * Minor improvements * Minor code fixes --- ...rt-with-labels-basic-config.component.html | 5 ++ ...hart-with-labels-basic-config.component.ts | 11 ++- ...e-series-chart-basic-config.component.html | 5 ++ ...ime-series-chart-basic-config.component.ts | 13 +++- .../bar-chart-with-labels-widget.component.ts | 1 + .../bar-chart-with-labels-widget.models.ts | 3 + .../widget/lib/chart/echarts-widget.models.ts | 6 ++ .../chart/time-series-chart-tooltip.models.ts | 4 +- .../time-series-chart-widget.component.ts | 1 + .../lib/chart/time-series-chart.models.ts | 2 + .../widget/lib/chart/time-series-chart.ts | 76 ++++++++++++++++++- ...with-labels-widget-settings.component.html | 5 ++ ...t-with-labels-widget-settings.component.ts | 10 ++- ...eries-chart-widget-settings.component.html | 5 ++ ...-series-chart-widget-settings.component.ts | 10 ++- .../widget/widget-container.component.html | 2 +- .../widget/widget-container.component.ts | 5 ++ .../home/models/widget-component.models.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 2 + 19 files changed, 157 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index f927340691..61398972c4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -98,6 +98,11 @@ {{ 'widgets.time-series-chart.data-zoom' | translate }}
+
+ + {{ 'widgets.time-series-chart.data-zoom-update-timewindow' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts index d569bcf37b..bfd430ad36 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts @@ -116,6 +116,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom iconColor: [configData.config.iconColor, []], dataZoom: [settings.dataZoom, []], + dataZoomUpdateTimewindow: [settings.dataZoomUpdateTimewindow, []], showBarLabel: [settings.showBarLabel, []], barLabelFont: [settings.barLabelFont, []], @@ -193,6 +194,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.dataZoomUpdateTimewindow = config.dataZoomUpdateTimewindow; this.widgetConfig.config.settings.showBarLabel = config.showBarLabel; this.widgetConfig.config.settings.barLabelFont = config.barLabelFont; @@ -248,10 +250,11 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom } protected validatorTriggers(): string[] { - return ['showTitle', 'showIcon', 'showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['dataZoom', 'showTitle', 'showIcon', 'showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean, trigger?: string) { + const dataZoom: boolean = this.barChartWidgetConfigForm.get('dataZoom').value; const showTitle: boolean = this.barChartWidgetConfigForm.get('showTitle').value; const showIcon: boolean = this.barChartWidgetConfigForm.get('showIcon').value; const showBarLabel: boolean = this.barChartWidgetConfigForm.get('showBarLabel').value; @@ -261,6 +264,12 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom const showTooltip: boolean = this.barChartWidgetConfigForm.get('showTooltip').value; const tooltipShowDate: boolean = this.barChartWidgetConfigForm.get('tooltipShowDate').value; + if (dataZoom) { + this.barChartWidgetConfigForm.get('dataZoomUpdateTimewindow').enable(); + } else { + this.barChartWidgetConfigForm.get('dataZoomUpdateTimewindow').disable(); + } + if (showTitle) { this.barChartWidgetConfigForm.get('title').enable(); this.barChartWidgetConfigForm.get('titleFont').enable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html index 0602e55fc9..fcba48e87a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html @@ -180,6 +180,11 @@ {{ 'widgets.time-series-chart.data-zoom' | translate }}
+
+ + {{ 'widgets.time-series-chart.data-zoom-update-timewindow' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts index 5b1410e5d7..143e7a682e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts @@ -96,7 +96,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon seriesMode = 'series'; predefinedValues = widgetTitleAutocompleteValues; - + constructor(protected store: Store, protected widgetConfigComponent: WidgetConfigComponent, private $injector: Injector, @@ -164,6 +164,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon iconColor: [configData.config.iconColor, []], dataZoom: [settings.dataZoom, []], + dataZoomUpdateTimewindow: [settings.dataZoomUpdateTimewindow, []], stack: [settings.stack, []], grid: [settings.grid, []], @@ -241,6 +242,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon this.widgetConfig.config.settings.thresholds = config.thresholds; this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.dataZoomUpdateTimewindow = config.dataZoomUpdateTimewindow; this.widgetConfig.config.settings.stack = config.stack; this.widgetConfig.config.settings.grid = config.grid; @@ -292,11 +294,12 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon } protected validatorTriggers(): string[] { - return ['comparisonEnabled', 'showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate', 'stack']; + return ['comparisonEnabled', 'dataZoom', 'showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate', 'stack']; } protected updateValidators(emitEvent: boolean, trigger?: string) { const comparisonEnabled: boolean = this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').value; + const dataZoom: boolean = this.timeSeriesChartWidgetConfigForm.get('dataZoom').value; const showTitle: boolean = this.timeSeriesChartWidgetConfigForm.get('showTitle').value; const showIcon: boolean = this.timeSeriesChartWidgetConfigForm.get('showIcon').value; const showLegend: boolean = this.timeSeriesChartWidgetConfigForm.get('showLegend').value; @@ -304,6 +307,12 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon const tooltipShowDate: boolean = this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').value; const stack: boolean = this.timeSeriesChartWidgetConfigForm.get('stack').value; + if (dataZoom) { + this.timeSeriesChartWidgetConfigForm.get('dataZoomUpdateTimewindow').enable(); + } else { + this.timeSeriesChartWidgetConfigForm.get('dataZoomUpdateTimewindow').disable(); + } + if (comparisonEnabled) { this.timeSeriesChartWidgetConfigForm.get('timeForComparison').enable(); this.timeSeriesChartWidgetConfigForm.get('comparisonCustomIntervalValue').enable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts index 0287b775c3..36ba031c8f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts @@ -114,6 +114,7 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft ngAfterViewInit() { const settings = barChartWithLabelsTimeSeriesSettings(this.settings); this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, settings, this.chartShape.nativeElement, this.renderer); + this.ctx.widgetActions = this.timeSeriesChart.getWidgetActions(); } ngOnDestroy() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index b8b879bf70..b045396f2c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -53,6 +53,7 @@ import { export interface BarChartWithLabelsWidgetSettings extends TimeSeriesChartTooltipWidgetSettings { dataZoom: boolean; + dataZoomUpdateTimewindow: boolean; showBarLabel: boolean; barLabelFont: Font; barLabelColor: string; @@ -79,6 +80,7 @@ export interface BarChartWithLabelsWidgetSettings extends TimeSeriesChartTooltip export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings = { dataZoom: false, + dataZoomUpdateTimewindow: false, showBarLabel: true, barLabelFont: { family: 'Roboto', @@ -181,6 +183,7 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings export const barChartWithLabelsTimeSeriesSettings = (settings: BarChartWithLabelsWidgetSettings): DeepPartial => ({ dataZoom: settings.dataZoom, + dataZoomUpdateTimewindow: settings.dataZoomUpdateTimewindow, grid: settings.grid, yAxes: { default: settings.yAxis diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts index 04f3b5d49b..ad57250036 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts @@ -262,3 +262,9 @@ export const getFocusedSeriesIndex = (chart: ECharts): number => { } return -1; }; + +export interface DataZoomEvent { + start: number; + end: number; + batch?: Array<{ start: number; end: number }>; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts index fe8c51bcee..91ce230539 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts @@ -293,7 +293,9 @@ private static mapTooltipParams(params: CallbackDataParams[] | CallbackDataParam TimeSeriesChartTooltip.appendTooltipItem(result, seriesParams, series); } else if (Array.isArray(params)) { for (seriesParams of params) { - TimeSeriesChartTooltip.appendTooltipItem(result, seriesParams, series); + if (seriesParams.value) { + TimeSeriesChartTooltip.appendTooltipItem(result, seriesParams, series); + } } } return result; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts index aa94ee717c..dc9ddf7cb8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts @@ -137,6 +137,7 @@ export class TimeSeriesChartWidgetComponent implements OnInit, OnDestroy, AfterV ngAfterViewInit() { this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, this.settings, this.chartShape.nativeElement, this.renderer); + this.ctx.widgetActions = this.timeSeriesChart.getWidgetActions(); } ngOnDestroy() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index e815eefe11..902e2189e2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -699,6 +699,7 @@ export interface TimeSeriesChartSettings extends TimeSeriesChartTooltipWidgetSet thresholds: TimeSeriesChartThreshold[]; darkMode: boolean; dataZoom: boolean; + dataZoomUpdateTimewindow: boolean; stack: boolean; grid: TimeSeriesChartGridSettings; yAxes: TimeSeriesChartYAxes; @@ -714,6 +715,7 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { thresholds: [], darkMode: false, dataZoom: true, + dataZoomUpdateTimewindow: false, stack: false, grid: mergeDeep({} as TimeSeriesChartGridSettings, timeSeriesChartGridDefaultSettings), diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index d1928d75f0..7f9f144b87 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { WidgetContext } from '@home/models/widget-component.models'; +import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; import { adjustTimeAxisExtentToData, calculateThresholdsOffset, @@ -23,7 +23,8 @@ import { createTimeSeriesYAxis, defaultTimeSeriesChartYAxisSettings, generateChartData, - LineSeriesStepType, normalizeAxisLimit, + LineSeriesStepType, + normalizeAxisLimit, parseThresholdData, TimeSeriesChartAxis, TimeSeriesChartDataItem, @@ -47,9 +48,11 @@ import { } from '@home/components/widget/lib/chart/time-series-chart.models'; import { calculateAxisSize, + DataZoomEvent, ECharts, echartsModule, EChartsOption, + getAxis, getAxisExtent, getFocusedSeriesIndex, measureAxisNameSize @@ -141,6 +144,13 @@ export class TbTimeSeriesChart { private hasVisualMap = false; private visualMapSelectedRanges: {[key: number]: boolean}; + private dataZoomResetting = false; + private dataZoomUpdatePending = false; + private dataZoomDebounce: ReturnType | null = null; + private lastDataZoomStart = 0; + private lastDataZoomEnd = 100; + private timewindowChanged = false; + private timeSeriesChart: ECharts; private timeSeriesChartOptions: EChartsOption; @@ -265,6 +275,12 @@ export class TbTimeSeriesChart { if (this.highlightedDataKey) { this.keyEnter(this.highlightedDataKey); } + if (this.dataZoomUpdatePending) { + this.dataZoomUpdatePending = false; + this.dataZoomResetting = true; + this.timeSeriesChart.dispatchAction({ type: 'dataZoom', start: 0, end: 100 }); + this.dataZoomResetting = false; + } } } @@ -386,6 +402,10 @@ export class TbTimeSeriesChart { } public destroy(): void { + if (this.dataZoomDebounce !== null) { + clearTimeout(this.dataZoomDebounce); + this.dataZoomDebounce = null; + } if (this.shapeResize$) { this.shapeResize$.disconnect(); } @@ -417,6 +437,25 @@ export class TbTimeSeriesChart { return this.darkMode; } + public getWidgetActions(): WidgetAction[] { + if (this.settings.dataZoom && this.settings.dataZoomUpdateTimewindow) { + return [{ + name: 'widgets.time-series-chart.reset-timewindow', + icon: 'restore', + onAction: () => { + if (this.dataZoomDebounce !== null) { + clearTimeout(this.dataZoomDebounce); + this.dataZoomDebounce = null; + } + this.timewindowChanged = false; + this.ctx.defaultSubscription.onResetTimewindow(); + }, + show: ()=> this.timewindowChanged + }]; + } + return []; + } + private setupData(): void { const noAggregationBarWidthSettings = this.settings.noAggregationBarWidthSettings; const targetBarWidth = noAggregationBarWidthSettings.strategy === TimeSeriesChartNoAggregationBarWidthStrategy.group ? @@ -846,8 +885,39 @@ export class TbTimeSeriesChart { this.updateAxes(false); if (this.settings.dataZoom) { - this.timeSeriesChart.on('datazoom', () => { + this.timeSeriesChart.on('datazoom', (event: DataZoomEvent) => { this.updateAxes(); + if (this.settings.dataZoomUpdateTimewindow && !this.dataZoomResetting) { + const evStart = event.batch?.length ? event.batch[0].start : event.start; + const evEnd = event.batch?.length ? event.batch[0].end : event.end; + this.lastDataZoomStart = evStart; + this.lastDataZoomEnd = evEnd; + if (this.dataZoomDebounce !== null) { + clearTimeout(this.dataZoomDebounce); + } + this.dataZoomDebounce = setTimeout(() => { + this.dataZoomDebounce = null; + if (this.timeSeriesChart?.isDisposed()) { + return; + } + if (Math.round(this.lastDataZoomStart) === 0 && Math.round(this.lastDataZoomEnd) === 100) { + this.ctx.defaultSubscription.onResetTimewindow(); + this.timewindowChanged = false; + } else { + const axis = getAxis(this.timeSeriesChart, 'xAxis', 'main'); + if (axis) { + const extent = axis.scale.getExtent(); + const startMs = Math.round(extent[0]); + const endMs = Math.round(extent[1]); + if (startMs < endMs) { + this.dataZoomUpdatePending = true; + this.timewindowChanged = true; + this.ctx.defaultSubscription.onUpdateTimewindow(startMs, endMs); + } + } + } + }, 500); + } }); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index bda194828e..793921b4dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -23,6 +23,11 @@ {{ 'widgets.time-series-chart.data-zoom' | translate }}
+
+ + {{ 'widgets.time-series-chart.data-zoom-update-timewindow' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts index 55ed3d5035..9cb686cf78 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts @@ -78,6 +78,7 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom this.barChartWidgetSettingsForm = this.fb.group({ dataZoom: [settings.dataZoom, []], + dataZoomUpdateTimewindow: [settings.dataZoomUpdateTimewindow, []], showBarLabel: [settings.showBarLabel, []], barLabelFont: [settings.barLabelFont, []], @@ -130,10 +131,11 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom } protected validatorTriggers(): string[] { - return ['showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['dataZoom', 'showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean) { + const dataZoom: boolean = this.barChartWidgetSettingsForm.get('dataZoom').value; const showBarLabel: boolean = this.barChartWidgetSettingsForm.get('showBarLabel').value; const showBarValue: boolean = this.barChartWidgetSettingsForm.get('showBarValue').value; const showBarBorder: boolean = this.barChartWidgetSettingsForm.get('showBarBorder').value; @@ -141,6 +143,12 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom const showTooltip: boolean = this.barChartWidgetSettingsForm.get('showTooltip').value; const tooltipShowDate: boolean = this.barChartWidgetSettingsForm.get('tooltipShowDate').value; + if (dataZoom) { + this.barChartWidgetSettingsForm.get('dataZoomUpdateTimewindow').enable(); + } else { + this.barChartWidgetSettingsForm.get('dataZoomUpdateTimewindow').disable(); + } + if (showBarLabel) { this.barChartWidgetSettingsForm.get('barLabelFont').enable(); this.barChartWidgetSettingsForm.get('barLabelColor').enable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html index b49d8ad902..c51bc07eb1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html @@ -86,6 +86,11 @@ {{ 'widgets.time-series-chart.data-zoom' | translate }}
+
+ + {{ 'widgets.time-series-chart.data-zoom-update-timewindow' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts index 8a96f31445..f20320a773 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts @@ -135,6 +135,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon thresholds: [settings.thresholds, []], dataZoom: [settings.dataZoom, []], + dataZoomUpdateTimewindow: [settings.dataZoomUpdateTimewindow, []], stack: [settings.stack, []], grid: [settings.grid, []], @@ -181,16 +182,23 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon } protected validatorTriggers(): string[] { - return ['comparisonEnabled', 'showLegend', 'showTooltip', 'tooltipShowDate', 'stack']; + return ['comparisonEnabled', 'dataZoom', 'showLegend', 'showTooltip', 'tooltipShowDate', 'stack']; } protected updateValidators(emitEvent: boolean) { const comparisonEnabled: boolean = this.timeSeriesChartWidgetSettingsForm.get('comparisonEnabled').value; + const dataZoom: boolean = this.timeSeriesChartWidgetSettingsForm.get('dataZoom').value; const showLegend: boolean = this.timeSeriesChartWidgetSettingsForm.get('showLegend').value; const showTooltip: boolean = this.timeSeriesChartWidgetSettingsForm.get('showTooltip').value; const tooltipShowDate: boolean = this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').value; const stack: boolean = this.timeSeriesChartWidgetSettingsForm.get('stack').value; + if (dataZoom) { + this.timeSeriesChartWidgetSettingsForm.get('dataZoomUpdateTimewindow').enable(); + } else { + this.timeSeriesChartWidgetSettingsForm.get('dataZoomUpdateTimewindow').disable(); + } + if (comparisonEnabled) { this.timeSeriesChartWidgetSettingsForm.get('timeForComparison').enable(); this.timeSeriesChartWidgetSettingsForm.get('comparisonCustomIntervalValue').enable(); 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 69cf1de06a..be2ef9d8cf 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 @@ -103,7 +103,7 @@ @for (action of widget.widgetActions; track action) {