This is an automated email from the ASF dual-hosted git repository. dominikriemer pushed a commit to branch improve-grouping-mode-of-time-series-chart in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 87d08b302118787049c671d64be82db86dc29bd2 Author: Dominik Riemer <[email protected]> AuthorDate: Thu Jun 11 23:03:22 2026 +0200 feat: Add coloring modes to time series chart in grouping mode --- ui/deployment/i18n/de.json | 3 + ui/deployment/i18n/en.json | 3 + ui/deployment/i18n/pl.json | 3 + .../color-mapping-options-config.component.html | 30 +++--- .../color-mapping-options-config.component.ts | 6 +- .../time-series-item-config.component.html | 66 +++++++++---- .../time-series-item-config.component.ts | 98 ++++++++++++++++--- .../time-series-chart-widget-config.component.ts | 30 +++++- .../model/time-series-chart-widget.model.ts | 10 ++ .../sp-timeseries-renderer.service.ts | 24 +++-- .../services/chart-colorization.service.ts | 106 ++++++++++++++------- 11 files changed, 290 insertions(+), 89 deletions(-) diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json index 922fcc8568..1afccd42a4 100644 --- a/ui/deployment/i18n/de.json +++ b/ui/deployment/i18n/de.json @@ -67,6 +67,7 @@ "Add and edit user accounts": "Benutzerkonten hinzufügen und bearbeiten", "Add condition": "Bedingung hinzufügen", "Add custom color mapping": "Benutzerdefiniertes Mapping hinzufügen", + "Custom color mapping": "Benutzerdefiniertes Farb-Mapping", "Add exact location": "Genauen Standort hinzufügen", "Add field": "Feld hinzufügen", "Add from template": "Aus Vorlage hinzufügen", @@ -489,6 +490,7 @@ "Group ID": "Gruppen-ID", "Group Name": "Name der Benutzergruppe", "Group by": "Gruppieren nach", + "Grouped colors": "Farben für Gruppierung", "Group name": "Name der Benutzergruppe", "Group selection": "Auswahl der Gruppe", "Grouped": "Gruppiert", @@ -1000,6 +1002,7 @@ "Start pipeline": "Pipeline starten", "Start pipeline immediately": "Pipeline sofort starten", "Start selected adapters": "Ausgewählte Adapter starten", + "Stable palette": "Stabile Palette", "Starting": "Starte", "Starting adapter": "Adapter wird gestartet", "Starting adapter {{adapterName}}": "Adapter starten {{adapterName}}", diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json index af22414388..5f0b743c7a 100644 --- a/ui/deployment/i18n/en.json +++ b/ui/deployment/i18n/en.json @@ -67,6 +67,7 @@ "Add and edit user accounts": null, "Add condition": null, "Add custom color mapping": null, + "Custom color mapping": null, "Add exact location": null, "Add field": null, "Add from template": null, @@ -489,6 +490,7 @@ "Group ID": null, "Group Name": null, "Group by": null, + "Grouped colors": null, "Group name": null, "Group selection": null, "Grouped": null, @@ -1000,6 +1002,7 @@ "Start pipeline": null, "Start pipeline immediately": null, "Start selected adapters": null, + "Stable palette": null, "Starting": null, "Starting adapter": null, "Starting adapter {{adapterName}}": "Starting adapter {{adapterName}}", diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json index 1f9fcbcd68..84f58a1a7c 100644 --- a/ui/deployment/i18n/pl.json +++ b/ui/deployment/i18n/pl.json @@ -67,6 +67,7 @@ "Add and edit user accounts": "Dodawaj i edytuj konta użytkowników", "Add condition": "Dodaj warunek", "Add custom color mapping": "Dodaj własne mapowanie kolorów", + "Custom color mapping": "Własne mapowanie kolorów", "Add exact location": "Dodaj dokładną lokalizację", "Add field": "Dodaj pole", "Add from template": "Dodaj z szablonu", @@ -489,6 +490,7 @@ "Group ID": "ID grupy", "Group Name": "Nazwa grupy", "Group by": "Grupuj według", + "Grouped colors": "Kolory grupowania", "Group name": "Nazwa grupy użytkowników", "Group selection": "Wybór grupy", "Grouped": "Zgrupowane", @@ -1000,6 +1002,7 @@ "Start pipeline": "Uruchom strumień", "Start pipeline immediately": "Uruchom strumień od razu", "Start selected adapters": "Uruchom wybrane adaptery", + "Stable palette": "Stabilna paleta", "Starting": "Uruchamianie", "Starting adapter": "Uruchamianie adaptera", "Starting adapter {{adapterName}}": "Uruchamianie adaptera {{adapterName}}", diff --git a/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.html b/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.html index df661e5716..b4767037d6 100644 --- a/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.html +++ b/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.html @@ -16,21 +16,23 @@ ~ --> <div fxLayout="column" fxLayoutGap="10px"> - <div - fxLayout="row" - fxLayoutGap="10px" - fxLayoutAlign="start center" - fxFlex="1" - class="checkbox-container" - > - <mat-checkbox - color="accent" - [(ngModel)]="showCustomColorMapping" - (ngModelChange)="setCustomColorMapping($event)" + @if (showToggle) { + <div + fxLayout="row" + fxLayoutGap="10px" + fxLayoutAlign="start center" + fxFlex="1" + class="checkbox-container" > - </mat-checkbox> - <small>{{ 'Add custom color mapping' | translate }}</small> - </div> + <mat-checkbox + color="accent" + [(ngModel)]="showCustomColorMapping" + (ngModelChange)="setCustomColorMapping($event)" + > + </mat-checkbox> + <small>{{ 'Add custom color mapping' | translate }}</small> + </div> + } @if (!isSelectedPropertyBoolean && showCustomColorMapping) { <div> diff --git a/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts b/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts index 03380eddb0..2cbdaf51c0 100644 --- a/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts +++ b/ui/src/app/chart-shared/components/chart-config/color-mapping-options-config/color-mapping-options-config.component.ts @@ -72,10 +72,12 @@ export class ColorMappingOptionsConfigComponent implements OnInit, OnChanges { @Input() colorMapping: { value: string; label: string; color: string }[]; - @Input() selectedProperty: DataExplorerField; + @Input() selectedProperty?: DataExplorerField; @Input() showCustomColorMapping: boolean; + @Input() showToggle = true; + @Output() viewRefreshEmitter: EventEmitter<void> = new EventEmitter<void>(); @@ -170,7 +172,7 @@ export class ColorMappingOptionsConfigComponent implements OnInit, OnChanges { } isBooleanPropertySelected(): boolean { - return this.selectedProperty.fieldCharacteristics.binary; + return this.selectedProperty?.fieldCharacteristics.binary ?? false; } setCustomColorMapping(showCustomColorMapping: boolean) { diff --git a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html index ac1ba8e095..1a64d5092d 100644 --- a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html +++ b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html @@ -38,7 +38,7 @@ [ngStyle]="{ background: currentlyConfiguredWidget.visualizationConfig.chosenColor[ - field.fullDbName + field.sourceIndex.toString() + getFieldKey(field) ], }" ></span> @@ -66,15 +66,11 @@ class="color-field" [(colorPicker)]=" currentlyConfiguredWidget.visualizationConfig - .chosenColor[ - field.fullDbName + field.sourceIndex.toString() - ] + .chosenColor[getFieldKey(field)] " [style.background]=" currentlyConfiguredWidget.visualizationConfig - .chosenColor[ - field.fullDbName + field.sourceIndex.toString() - ] + .chosenColor[getFieldKey(field)] " required [cpPosition]="'bottom'" @@ -97,10 +93,7 @@ <input [(ngModel)]=" currentlyConfiguredWidget.visualizationConfig - .displayName[ - field.fullDbName + - field.sourceIndex.toString() - ] + .displayName[getFieldKey(field)] " matInput (input)=" @@ -124,10 +117,7 @@ <mat-select [(value)]=" currentlyConfiguredWidget.visualizationConfig - .displayType[ - field.fullDbName + - field.sourceIndex.toString() - ] + .displayType[getFieldKey(field)] " (selectionChange)="viewRefreshEmitter.emit()" > @@ -163,10 +153,7 @@ <mat-select [(value)]=" currentlyConfiguredWidget.visualizationConfig - .chosenAxis[ - field.fullDbName + - field.sourceIndex.toString() - ] + .chosenAxis[getFieldKey(field)] " (selectionChange)="viewRefreshEmitter.emit()" > @@ -180,6 +167,47 @@ </mat-form-field> </div> </div> + @if (hasGrouping(field)) { + <div fxLayout="row" fxFlex="100"> + <div fxFlex="30" fxLayoutAlign="start center"> + <small>{{ 'Grouped colors' | translate }}</small> + </div> + <div fxFlex="70" fxLayoutAlign="start center"> + <mat-form-field + appearance="outline" + color="accent" + class="w-100" + > + <mat-select + [value]="getGroupedColorMode(field)" + (selectionChange)=" + setGroupedColorMode(field, $event.value) + " + > + <mat-option [value]="'stable_palette'">{{ + 'Stable palette' | translate + }}</mat-option> + <mat-option [value]="'custom_mapping'">{{ + 'Custom color mapping' | translate + }}</mat-option> + </mat-select> + </mat-form-field> + </div> + </div> + @if (getGroupedColorMode(field) === 'custom_mapping') { + <sp-color-mapping-options-config + class="mt-sm" + [colorMapping]="getGroupedColorMappings(field)" + [showCustomColorMapping]="true" + [showToggle]="false" + (colorMappingChange)=" + setGroupedColorMappings(field, $event) + " + (viewRefreshEmitter)="viewRefreshEmitter.emit()" + > + </sp-color-mapping-options-config> + } + } </div> } </div> diff --git a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts index f15ce41305..fc23ef2c40 100644 --- a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts +++ b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts @@ -18,16 +18,19 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { DataExplorerField } from '@streampipes/platform-services'; -import { TimeSeriesChartWidgetModel } from '../../../charts/time-series-chart/model/time-series-chart-widget.model'; import { - DefaultFlexDirective, - DefaultLayoutAlignDirective, - DefaultLayoutDirective, - DefaultLayoutGapDirective, + TimeSeriesChartWidgetModel, + TimeSeriesGroupColorMapping, + TimeSeriesGroupedColorMode, +} from '../../../charts/time-series-chart/model/time-series-chart-widget.model'; +import { + FlexDirective, + LayoutAlignDirective, + LayoutGapDirective, } from '@ngbracket/ngx-layout/flex'; import { MatCheckbox } from '@angular/material/checkbox'; import { NgStyle } from '@angular/common'; -import { DefaultStyleDirective } from '@ngbracket/ngx-layout/extended'; +import { StyleDirective } from '@ngbracket/ngx-layout/extended'; import { MatIconButton } from '@angular/material/button'; import { MatTooltip } from '@angular/material/tooltip'; import { ColorPickerDirective } from 'ngx-color-picker'; @@ -36,21 +39,23 @@ import { MatInput } from '@angular/material/input'; import { FormsModule } from '@angular/forms'; import { MatOption, MatSelect } from '@angular/material/select'; import { TranslatePipe } from '@ngx-translate/core'; +import { ColorMappingOptionsConfigComponent } from '../../color-mapping-options-config/color-mapping-options-config.component'; +import { LayoutDirective } from '@ngbracket/ngx-layout'; @Component({ selector: 'sp-time-series-item-config', templateUrl: './time-series-item-config.component.html', styleUrls: ['./time-series-item-config.component.scss'], imports: [ - DefaultLayoutDirective, - DefaultLayoutAlignDirective, + LayoutGapDirective, + LayoutAlignDirective, MatCheckbox, - DefaultFlexDirective, + FlexDirective, NgStyle, - DefaultStyleDirective, + StyleDirective, MatIconButton, MatTooltip, - DefaultLayoutGapDirective, + LayoutGapDirective, ColorPickerDirective, MatFormField, MatInput, @@ -58,6 +63,8 @@ import { TranslatePipe } from '@ngx-translate/core'; MatSelect, MatOption, TranslatePipe, + ColorMappingOptionsConfigComponent, + LayoutDirective, ], }) export class SpTimeseriesItemConfigComponent { @@ -122,4 +129,73 @@ export class SpTimeseriesItemConfigComponent { ] = searchValue; this.viewRefreshEmitter.emit(); } + + getFieldKey(field: DataExplorerField): string { + return field.fullDbName + field.sourceIndex.toString(); + } + + hasGrouping(field: DataExplorerField): boolean { + return ( + this.currentlyConfiguredWidget.dataConfig.sourceConfigs[ + field.sourceIndex + ]?.queryConfig.groupBy?.some( + groupByField => groupByField.selected, + ) ?? false + ); + } + + getGroupedColorMode(field: DataExplorerField): TimeSeriesGroupedColorMode { + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMode ??= + {}; + return ( + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMode[ + this.getFieldKey(field) + ] ?? 'stable_palette' + ); + } + + setGroupedColorMode( + field: DataExplorerField, + mode: TimeSeriesGroupedColorMode, + ): void { + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMode ??= + {}; + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMode[ + this.getFieldKey(field) + ] = mode; + this.viewRefreshEmitter.emit(); + } + + getGroupedColorMappings( + field: DataExplorerField, + ): TimeSeriesGroupColorMapping[] { + const fieldKey = this.getFieldKey(field); + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMappings ??= + {}; + const mappings = + this.currentlyConfiguredWidget.visualizationConfig + .groupedColorMappings[fieldKey]; + + if (!mappings) { + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMappings[ + fieldKey + ] = []; + return this.currentlyConfiguredWidget.visualizationConfig + .groupedColorMappings[fieldKey]; + } + + return mappings; + } + + setGroupedColorMappings( + field: DataExplorerField, + mappings: TimeSeriesGroupColorMapping[], + ): void { + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMappings ??= + {}; + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMappings[ + this.getFieldKey(field) + ] = mappings; + this.viewRefreshEmitter.emit(); + } } diff --git a/ui/src/app/chart-shared/components/charts/time-series-chart/config/time-series-chart-widget-config.component.ts b/ui/src/app/chart-shared/components/charts/time-series-chart/config/time-series-chart-widget-config.component.ts index 1b33620f18..2c8bb69567 100644 --- a/ui/src/app/chart-shared/components/charts/time-series-chart/config/time-series-chart-widget-config.component.ts +++ b/ui/src/app/chart-shared/components/charts/time-series-chart/config/time-series-chart-widget-config.component.ts @@ -84,6 +84,12 @@ export class TimeSeriesChartWidgetConfigComponent extends BaseWidgetConfig< this.currentlyConfiguredWidget.visualizationConfig.displayType; const currentAxis = this.currentlyConfiguredWidget.visualizationConfig.chosenAxis; + const currentGroupedColorMode = + this.currentlyConfiguredWidget.visualizationConfig + .groupedColorMode ?? {}; + const currentGroupedColorMappings = + this.currentlyConfiguredWidget.visualizationConfig + .groupedColorMappings ?? {}; const lenBefore = Object.keys(currentAxis).length; @@ -94,6 +100,8 @@ export class TimeSeriesChartWidgetConfigComponent extends BaseWidgetConfig< currentNames[name] = field.fullDbName; currentTypes[name] = 'lines'; currentAxis[name] = 'left'; + currentGroupedColorMode[name] = 'stable_palette'; + currentGroupedColorMappings[name] = []; } }); @@ -105,6 +113,10 @@ export class TimeSeriesChartWidgetConfigComponent extends BaseWidgetConfig< currentTypes; this.currentlyConfiguredWidget.visualizationConfig.chosenAxis = currentAxis; + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMode = + currentGroupedColorMode; + this.currentlyConfiguredWidget.visualizationConfig.groupedColorMappings = + currentGroupedColorMappings; this.triggerViewRefresh(); } @@ -140,6 +152,16 @@ export class TimeSeriesChartWidgetConfigComponent extends BaseWidgetConfig< this.numericPlusBooleanFields, () => 'left', ); + config.groupedColorMode = this.getConfigOrDefault( + config.groupedColorMode, + this.numericPlusBooleanFields, + () => 'stable_palette', + ); + config.groupedColorMappings = this.getConfigOrDefault( + config.groupedColorMappings, + this.numericPlusBooleanFields, + () => [], + ); config.yKeys = []; config.selectedTimeSeriesChartProperties = @@ -165,11 +187,11 @@ export class TimeSeriesChartWidgetConfigComponent extends BaseWidgetConfig< }; } - private getConfigOrDefault( - config: Record<string, any>, + private getConfigOrDefault<T>( + config: Record<string, T>, availableFields: DataExplorerField[], - getDefaultValue: (field: DataExplorerField, index: number) => string, - ) { + getDefaultValue: (field: DataExplorerField, index: number) => T, + ): Record<string, T> { const fieldKeys = availableFields.map( f => f.fullDbName + f.sourceIndex, ); diff --git a/ui/src/app/chart-shared/components/charts/time-series-chart/model/time-series-chart-widget.model.ts b/ui/src/app/chart-shared/components/charts/time-series-chart/model/time-series-chart-widget.model.ts index 4cc95bc86f..24a3a89bb4 100644 --- a/ui/src/app/chart-shared/components/charts/time-series-chart/model/time-series-chart-widget.model.ts +++ b/ui/src/app/chart-shared/components/charts/time-series-chart/model/time-series-chart-widget.model.ts @@ -27,6 +27,14 @@ import { DataExplorerVisConfig, } from '../../../../models/dataview-dashboard.model'; +export type TimeSeriesGroupedColorMode = 'stable_palette' | 'custom_mapping'; + +export interface TimeSeriesGroupColorMapping { + value: string; + label: string; + color: string; +} + export interface TimeSeriesChartVisConfig extends DataExplorerVisConfig { selectedTimeSeriesChartProperties: DataExplorerField[]; selectedBackgroundColorProperty?: EventPropertyUnion; @@ -39,6 +47,8 @@ export interface TimeSeriesChartVisConfig extends DataExplorerVisConfig { displayName: { [id: string]: string }; displayType: { [id: string]: string }; chosenAxis: { [id: string]: string }; + groupedColorMode: { [id: string]: TimeSeriesGroupedColorMode }; + groupedColorMappings: { [id: string]: TimeSeriesGroupColorMapping[] }; leftAxis: AxisConfig; rightAxis: AxisConfig; showSpike: boolean; diff --git a/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts b/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts index cdc652878a..ebd417bfa5 100644 --- a/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts +++ b/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts @@ -27,7 +27,11 @@ import { Injectable } from '@angular/core'; import { TimeSeriesChartWidgetModel } from './model/time-series-chart-widget.model'; import { DataExplorerField } from '@streampipes/platform-services'; import { SpBaseEchartsRenderer } from '../../../echarts-renderer/base-echarts-renderer'; -import { GeneratedDataset, WidgetSize } from '../../../models/dataset.model'; +import { + GeneratedDataset, + TagValue, + WidgetSize, +} from '../../../models/dataset.model'; import { AxisConfig, WidgetBaseAppearanceConfig, @@ -71,12 +75,18 @@ export class SpTimeseriesRendererService extends SpBaseEchartsRenderer<TimeSerie widgetConfig.visualizationConfig.displayName[ field.fullDbName + field.sourceIndex ]; + const mappedGroupLabel = this.colorizationService.findLabel( + widgetConfig.visualizationConfig, + field, + tag, + ); const seriesName = dataset.groupedDatasets.length > 0 - ? this.echartsUtilsService.toTagString( + ? (mappedGroupLabel ?? + this.echartsUtilsService.toTagString( tag, displayName, - ) + )) : displayName; const fieldIndex = rawDatasetDimensions.indexOf( field.fullDbName, @@ -85,10 +95,10 @@ export class SpTimeseriesRendererService extends SpBaseEchartsRenderer<TimeSerie this.makeSeries( widgetConfig, i, - groupIndex, field, fieldIndex, seriesName, + tag, ), ); } @@ -136,19 +146,19 @@ export class SpTimeseriesRendererService extends SpBaseEchartsRenderer<TimeSerie makeSeries( widgetConfig: TimeSeriesChartWidgetModel, datasetIndex: number, - groupIndex: number, field: DataExplorerField, fieldIndex: number, seriesName: string, + tag?: TagValue, ): SeriesOption { const seriesType = this.makeSeriesType( widgetConfig.visualizationConfig.displayType, field, ); const color = this.colorizationService.makeColor( - widgetConfig.visualizationConfig.chosenColor, + widgetConfig.visualizationConfig, field, - groupIndex, + tag, ); const series = { diff --git a/ui/src/app/chart-shared/services/chart-colorization.service.ts b/ui/src/app/chart-shared/services/chart-colorization.service.ts index 35817bc944..eb2a3057b1 100644 --- a/ui/src/app/chart-shared/services/chart-colorization.service.ts +++ b/ui/src/app/chart-shared/services/chart-colorization.service.ts @@ -18,53 +18,95 @@ import { Injectable } from '@angular/core'; import { DataExplorerField } from '@streampipes/platform-services'; +import { + TimeSeriesChartVisConfig, + TimeSeriesGroupColorMapping, +} from '../components/charts/time-series-chart/model/time-series-chart-widget.model'; +import { TagValue } from '../models/dataset.model'; +import { ColorMappingService } from './color-mapping.service'; @Injectable({ providedIn: 'root' }) export class ChartColorizationService { + constructor(private colorMappingService: ColorMappingService) {} + makeColor( - chosenColor: Record<string, string>, + visualizationConfig: TimeSeriesChartVisConfig, field: DataExplorerField, - groupIndex: number, + tag?: TagValue, ): string { - const baseColor = chosenColor[field.fullDbName + field.sourceIndex]; - return this.adjustColorBrightness(baseColor, groupIndex); + const fieldKey = field.fullDbName + field.sourceIndex; + const baseColor = visualizationConfig.chosenColor[fieldKey]; + + if (!tag || tag.values.length === 0) { + return baseColor; + } + + const groupKey = this.makeGroupKey(tag); + const fieldMappings = + visualizationConfig.groupedColorMappings?.[fieldKey] ?? []; + + if ( + visualizationConfig.groupedColorMode?.[fieldKey] === + 'custom_mapping' + ) { + return ( + this.findMapping(fieldMappings, tag, groupKey)?.color ?? + this.colorMappingService.getDefaultColor(groupKey) + ); + } + + return this.colorMappingService.getDefaultColor( + `${fieldKey}:${groupKey}`, + ); } - private adjustColorBrightness(color: string, groupIndex: number): string { - const amount = groupIndex * 15; - let { r, g, b } = this.hexToRgb(color); + findLabel( + visualizationConfig: TimeSeriesChartVisConfig, + field: DataExplorerField, + tag?: TagValue, + ): string | undefined { + if (!tag || tag.values.length === 0) { + return undefined; + } - r = this.clampColorValue(r + amount); - g = this.clampColorValue(g + amount); - b = this.clampColorValue(b + amount); + const fieldKey = field.fullDbName + field.sourceIndex; + const fieldMappings = + visualizationConfig.groupedColorMappings?.[fieldKey] ?? []; + const mapping = this.findMapping( + fieldMappings, + tag, + this.makeGroupKey(tag), + ); - return this.rgbToHex(r, g, b); + return mapping?.label?.trim() ? mapping.label : undefined; } - private hexToRgb(hex: string): { r: number; g: number; b: number } { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16), - } - : null; + private findMapping( + mappings: TimeSeriesGroupColorMapping[], + tag: TagValue, + groupKey: string, + ): TimeSeriesGroupColorMapping | undefined { + const candidateKeys = this.makeCandidateKeys(tag, groupKey); + return mappings.find(mapping => candidateKeys.includes(mapping.value)); } - private rgbToHex(r: number, g: number, b: number): string { - return ( - '#' + - [r, g, b] - .map(x => { - const hex = x.toString(16); - return hex.length === 1 ? '0' + hex : hex; - }) - .join('') - ); + private makeGroupKey(tag: TagValue): string { + return tag.tagKeys + .map((key, index) => `${key}=${tag.values[index]}`) + .join(', '); } - private clampColorValue(value: number): number { - return Math.max(0, Math.min(255, value)); + private makeCandidateKeys(tag: TagValue, groupKey: string): string[] { + const valueStrings = tag.values.map(value => String(value)); + const keyValuePairs = tag.tagKeys.map( + (key, index) => `${key}=${valueStrings[index]}`, + ); + + return [ + groupKey, + valueStrings.join(', '), + ...keyValuePairs, + ...valueStrings, + ]; } }
