This is an automated email from the ASF dual-hosted git repository. dominikriemer pushed a commit to branch improve-chart-selection-loading in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 6caa3c82557f8839bdf03a7cf196be7c210d7598 Author: Dominik Riemer <[email protected]> AuthorDate: Wed Jun 17 15:17:21 2026 +0200 feat: Improve performance of chart fetching --- .../chart-selection-panel.component.html | 2 +- .../chart-selection-panel.component.scss | 5 ++ .../chart-preview/chart-preview.component.html | 61 ++++++---------------- .../chart-preview/chart-preview.component.scss | 32 +++++++++++- .../chart-preview/chart-preview.component.ts | 22 ++------ .../chart-selection/chart-selection.component.html | 21 +++++--- .../chart-selection/chart-selection.component.scss | 29 +++++++++- .../chart-selection/chart-selection.component.ts | 31 ++++++++++- .../panel/dashboard-panel.component.html | 12 ++++- .../panel/dashboard-panel.component.scss | 21 ++++++++ 10 files changed, 161 insertions(+), 75 deletions(-) diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html index 37166e0a2e..e1f0fd1508 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html @@ -27,7 +27,7 @@ data-cy="designer-panel-data-config" [label]="'Charts' | translate" > - <div class="scroll-tab-content"> + <div class="scroll-tab-content chart-selection-tab-content"> <sp-chart-selection (addChartEmitter)="addChartEmitter.emit($event)" fxFlex="100" diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.scss b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.scss index e280dd74d5..1ef662e462 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.scss +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.scss @@ -15,3 +15,8 @@ * limitations under the License. * */ + +.chart-selection-tab-content { + height: 100%; + overflow-y: hidden; +} diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html index 56576d9cf0..29eddbab1d 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html @@ -17,48 +17,27 @@ --> <div - fxLayout="row" - fxLayoutAlign="space-between center" - fxLayoutGap="12px" class="data-view-preview-outer" - [attr.data-cy]="'add-data-view-btn-' + chart.name.replaceAll(' ', '')" + [attr.data-cy]="dataCyId" + [attr.title]="chart.name" (click)="addChart()" > - <div - fxLayout="row" - fxLayoutAlign="start center" - fxLayoutGap="12px" - fxFlex - class="chart-preview-main" - > - <div - class="chart-preview-icon-shell" - fxLayout="row" - fxLayoutAlign="center center" - [matTooltip]="widgetTypeLabel" - > + <div class="chart-preview-header"> + <div class="chart-preview-icon-shell" [attr.title]="widgetTypeLabel"> <mat-icon>{{ widgetTypeIcon }}</mat-icon> </div> - <div fxFlex fxLayout="column" class="chart-preview-copy"> - <div - fxLayout="row" - fxLayoutAlign="start center" - fxLayoutGap="8px" - class="chart-preview-title-row" - [matTooltip]="chart.name" - > - <h5 fxFlex class="chart-preview-title">{{ chart.name }}</h5> + <div class="chart-preview-copy"> + <div class="chart-preview-title-row"> + <h5 class="chart-preview-title">{{ chart.name }}</h5> </div> - <div - fxLayout="row" - fxLayoutAlign="start center" - fxLayoutGap="12px" - class="chart-preview-meta" - > + <div class="chart-preview-meta"> @if (chart.datasetName) { - <span class="chart-preview-meta-item chart-preview-dataset"> + <span + class="chart-preview-meta-item chart-preview-dataset" + [attr.title]="chart.datasetName" + > {{ chart.datasetName }} </span> } @@ -66,26 +45,18 @@ </div> </div> - <div - fxLayout="row" - fxLayoutAlign="end center" - fxLayoutGap="4px" - class="chart-preview-actions" - > + <div class="chart-preview-actions"> <button mat-icon-button type="button" - [matTooltip]="'Preview chart' | translate" + [attr.aria-label]="'Preview chart' | translate" + [attr.title]="'Preview chart' | translate" (click)="openPreview($event)" data-cy="chart-preview-feature-card" > <mat-icon>visibility</mat-icon> </button> - <div - class="chart-preview-add-indicator" - fxLayout="row" - fxLayoutAlign="center center" - > + <div class="chart-preview-add-indicator"> <mat-icon>add</mat-icon> </div> </div> diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.scss b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.scss index 5282cd1fc1..3d7a82ba85 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.scss +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.scss @@ -17,6 +17,10 @@ */ .data-view-preview-outer { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 8px; min-height: 72px; border: 1px solid color-mix(in srgb, var(--color-bg-3) 68%, transparent); border-radius: 14px; @@ -41,7 +45,6 @@ ); background: color-mix(in srgb, var(--color-bg-1) 86%, white 14%); box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); - transform: translateY(-1px); cursor: pointer; } @@ -50,6 +53,9 @@ mat-icon { } .chart-preview-icon-shell { + display: flex; + align-items: center; + justify-content: center; width: 40px; min-width: 40px; height: 40px; @@ -63,11 +69,23 @@ mat-icon { } .chart-preview-main { + display: contents; +} + +.chart-preview-header { + display: flex; + align-items: center; + gap: 12px; min-width: 0; + overflow: hidden; } .chart-preview-copy { + display: flex; + flex: 1 1 auto; + flex-direction: column; min-width: 0; + overflow: hidden; } .chart-preview-title-row { @@ -88,13 +106,16 @@ mat-icon { .chart-preview-meta { min-width: 0; + display: block; width: 100%; + overflow: hidden; margin-top: 2px; color: var(--color-paragraph); font-size: var(--font-size-sm); } .chart-preview-meta-item { + display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -113,10 +134,19 @@ mat-icon { } .chart-preview-actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 4px; flex-shrink: 0; + width: 100%; + margin-left: 0; } .chart-preview-add-indicator { + display: flex; + align-items: center; + justify-content: center; width: 28px; height: 28px; border-radius: 50%; diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts index 153a49c417..4ca3306b26 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts @@ -17,6 +17,7 @@ */ import { + ChangeDetectionStrategy, Component, EventEmitter, Input, @@ -26,15 +27,8 @@ import { } from '@angular/core'; import { ChartSummaryDto } from '@streampipes/platform-services'; import { ChartRegistry } from '../../../../../../chart-shared/registry/chart-registry.service'; -import { - FlexDirective, - LayoutAlignDirective, - LayoutDirective, - LayoutGapDirective, -} from '@ngbracket/ngx-layout/flex'; import { MatIcon } from '@angular/material/icon'; import { MatIconButton } from '@angular/material/button'; -import { MatTooltip } from '@angular/material/tooltip'; import { FeatureCardService } from '@streampipes/shared-ui'; import { TranslatePipe } from '@ngx-translate/core'; @@ -42,16 +36,8 @@ import { TranslatePipe } from '@ngx-translate/core'; selector: 'sp-chart-preview', templateUrl: './chart-preview.component.html', styleUrls: ['./chart-preview.component.scss'], - imports: [ - LayoutDirective, - FlexDirective, - LayoutGapDirective, - LayoutAlignDirective, - MatIcon, - MatIconButton, - MatTooltip, - TranslatePipe, - ], + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [MatIcon, MatIconButton, TranslatePipe], }) export class ChartPreviewComponent implements OnInit { private widgetRegistryService = inject(ChartRegistry); @@ -62,6 +48,7 @@ export class ChartPreviewComponent implements OnInit { widgetTypeLabel = ''; widgetTypeIcon = 'insert_chart'; + dataCyId = ''; @Output() addChartEmitter: EventEmitter<string> = new EventEmitter<string>(); @@ -72,6 +59,7 @@ export class ChartPreviewComponent implements OnInit { ); this.widgetTypeLabel = template?.label ?? this.chart.widgetType; this.widgetTypeIcon = template?.icon ?? 'insert_chart'; + this.dataCyId = `add-data-view-btn-${this.chart.name.replaceAll(' ', '')}`; } addChart(): void { diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html index d77aeb90f0..8fd0b96a9e 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html @@ -100,20 +100,27 @@ </div> @if (charts.length > 0 && filteredCharts.length > 0) { - <div + <cdk-virtual-scroll-viewport fxFlexFill - fxLayout="column" - fxLayoutGap="8px" - class="chart-selection-results" + class="chart-selection-results chart-selection-viewport" + [itemSize]="chartItemSize" + [minBufferPx]="chartItemSize * 4" + [maxBufferPx]="chartItemSize * 8" > - @for (chart of filteredCharts; track chart.elementId) { + <div + *cdkVirtualFor=" + let chart of filteredCharts; + trackBy: trackByChartId + " + class="chart-selection-item" + > <sp-chart-preview [chart]="chart" (addChartEmitter)="addChartEmitter.emit($event)" > </sp-chart-preview> - } - </div> + </div> + </cdk-virtual-scroll-viewport> } @else if (charts.length === 0) { <div fxLayoutAlign="center center" fxLayout="column" fxFlex="100"> <span class="no-widget-hint">{{ diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.scss b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.scss index 445d919e70..50eee74146 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.scss +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.scss @@ -17,7 +17,12 @@ */ .chart-selection-shell { + box-sizing: border-box; + display: flex; + flex-direction: column; height: 100%; + min-height: 0; + overflow: hidden; padding: 6px 6px 8px; background: linear-gradient( 180deg, @@ -27,6 +32,7 @@ } .chart-selection-toolbar { + flex: 0 0 auto; position: sticky; top: 0; z-index: 2; @@ -64,7 +70,28 @@ } .chart-selection-results { - padding-top: 2px; + flex: 1 1 0; + min-height: 0; + padding-top: 0; +} + +.chart-selection-viewport { + box-sizing: border-box; + width: 100%; + overflow-y: auto; + overflow-x: hidden; +} + +.chart-selection-item { + box-sizing: border-box; + width: 100%; + height: 132px; + padding: 0 0 6px; +} + +.chart-selection-viewport ::ng-deep .cdk-virtual-scroll-content-wrapper { + width: 100%; + max-width: 100%; } .chart-selection-empty { diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts index 9059f2b978..e9a346177f 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts @@ -16,7 +16,15 @@ * */ -import { Component, EventEmitter, inject, OnInit, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + inject, + OnInit, + Output, +} from '@angular/core'; import { ChartService, ChartSummaryDto } from '@streampipes/platform-services'; import { AuthService } from '../../../../../services/auth.service'; import { UserPrivilege } from '../../../../../core/auth/user-privilege.enum'; @@ -40,11 +48,17 @@ import { } from '@angular/material/form-field'; import { MatInput } from '@angular/material/input'; import { MatTooltip } from '@angular/material/tooltip'; +import { + CdkFixedSizeVirtualScroll, + CdkVirtualForOf, + CdkVirtualScrollViewport, +} from '@angular/cdk/scrolling'; @Component({ selector: 'sp-chart-selection', templateUrl: './chart-selection.component.html', styleUrls: ['./chart-selection.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, imports: [ FlexDirective, FlexFillDirective, @@ -61,6 +75,9 @@ import { MatTooltip } from '@angular/material/tooltip'; MatInput, TranslatePipe, MatTooltip, + CdkVirtualScrollViewport, + CdkFixedSizeVirtualScroll, + CdkVirtualForOf, ], }) export class ChartSelectionComponent implements OnInit { @@ -68,6 +85,7 @@ export class ChartSelectionComponent implements OnInit { private authService = inject(AuthService); private chartRegistryService = inject(ChartRegistry); private chartRoutingService = inject(ChartRoutingService); + private cdr = inject(ChangeDetectorRef); @Output() addChartEmitter: EventEmitter<string> = new EventEmitter(); @@ -76,6 +94,7 @@ export class ChartSelectionComponent implements OnInit { filteredCharts: ChartSummaryDto[] = []; searchTerm = ''; isRefreshing = false; + readonly chartItemSize = 132; hasChartWritePrivileges: boolean = false; @@ -93,18 +112,22 @@ export class ChartSelectionComponent implements OnInit { refreshCharts(): void { this.isRefreshing = true; + this.cdr.markForCheck(); this.dataViewService.getChartSummary().subscribe({ next: chartSummary => { this.charts = chartSummary.resources.sort((a, b) => a.name.localeCompare(b.name), ); this.applySearch(); + this.cdr.markForCheck(); }, complete: () => { this.isRefreshing = false; + this.cdr.markForCheck(); }, error: () => { this.isRefreshing = false; + this.cdr.markForCheck(); }, }); } @@ -112,17 +135,23 @@ export class ChartSelectionComponent implements OnInit { onSearchTermChanged(value: string): void { this.searchTerm = value; this.applySearch(); + this.cdr.markForCheck(); } clearSearch(): void { this.searchTerm = ''; this.applySearch(); + this.cdr.markForCheck(); } hasActiveSearch(): boolean { return this.searchTerm.trim().length > 0; } + trackByChartId(index: number, chart: ChartSummaryDto): string { + return chart.elementId; + } + private applySearch(): void { const query = this.searchTerm.trim().toLowerCase(); if (!query) { diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html index 86eda57b85..d60106f42e 100644 --- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html +++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html @@ -75,17 +75,23 @@ position="end" class="designer-panel" > - <div fxLayout="column" fxFlex="100"> + <div + fxLayout="column" + fxFlex="100" + style="min-height: 0; overflow: hidden" + > @if (editMode) { <div fxFlex="100" class="designer-panel-content" fxLayout="column" + style="min-height: 0" > <div fxFlex="100" fxLayout="column" class="no-overflow" + style="min-height: 0" > <mat-tab-group color="accent" @@ -99,7 +105,9 @@ data-cy="designer-panel-data-config" [label]="'Charts' | translate" > - <div class="scroll-tab-content"> + <div + class="scroll-tab-content chart-selection-tab-content" + > <sp-chart-selection (addChartEmitter)=" addChartToDashboard( diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss b/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss index 3708772e94..2655e51d21 100644 --- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss +++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss @@ -45,6 +45,27 @@ .designer-panel { width: 350px; + overflow: hidden; +} + +.chart-selection-tab-content { + height: 100%; + overflow-y: hidden; +} + +.designer-panel ::ng-deep .mat-drawer-inner-container { + overflow: hidden; +} + +.designer-panel ::ng-deep .mat-mdc-tab-body-wrapper, +.designer-panel ::ng-deep .mat-mdc-tab-body, +.designer-panel ::ng-deep .mat-mdc-tab-body-content { + height: 100%; + min-height: 0; +} + +.designer-panel ::ng-deep .mat-mdc-tab-body-content { + overflow: hidden; } .panel-toggle-button {
