From 295a841208765a3c0f618e8178449403809313ec Mon Sep 17 00:00:00 2001 From: MatthieuBarbet Date: Thu, 10 Aug 2023 14:24:26 +0200 Subject: [PATCH] Feat: enhance color service --- .../src/lib/components/componentsUtils.ts | 15 ++++---- .../components/donut/donut.component.spec.ts | 13 ++++--- .../lib/components/donut/donut.component.ts | 5 +++ .../histogram/histogram.component.spec.ts | 16 ++++++--- .../mapgl-legend.component.spec.ts | 14 +++++--- .../powerbars/powerbars.component.spec.ts | 12 +++++-- .../powerbars/powerbars.component.ts | 21 ++++++++--- .../result-item/result-item.component.html | 5 +-- .../result-item/result-item.component.spec.ts | 13 +++++-- .../result-item/result-item.component.ts | 36 +++++++++++++++---- .../result-list/result-list.component.spec.ts | 16 ++++++--- .../lib/services/color.generator.service.ts | 9 ++++- 12 files changed, 129 insertions(+), 46 deletions(-) diff --git a/projects/arlas-components/src/lib/components/componentsUtils.ts b/projects/arlas-components/src/lib/components/componentsUtils.ts index 45eddcc5..3695c3e5 100644 --- a/projects/arlas-components/src/lib/components/componentsUtils.ts +++ b/projects/arlas-components/src/lib/components/componentsUtils.ts @@ -20,6 +20,7 @@ import { mix } from 'tinycolor2'; import { isNumber } from 'util'; import { UntypedFormControl, Validators } from '@angular/forms'; +import { Observable, Subject } from 'rxjs'; export function formatNumber(x, formatChar = ' '): string { if (formatChar === NUMBER_FORMAT_CHAR) { @@ -67,8 +68,9 @@ export function getValues(map): Array { } export abstract class ColorGeneratorLoader { - public abstract keysToColors: Array>; - public abstract colorsSaturationWeight: number ; + public abstract keysToColors: Array<[string, string]>; + public abstract colorsSaturationWeight: number; + public abstract changekeysToColors$: Observable; /** * This method generates a determistic color from the given key, a list of [key, color] and a saturation weight. * @param key The text from which the color is generated @@ -81,9 +83,9 @@ export abstract class ColorGeneratorLoader { } export class AwcColorGeneratorLoader extends ColorGeneratorLoader { - public keysToColors: Array>; - public colorsSaturationWeight = 0.5 ; - + public changekeysToColors$: Observable = new Subject().asObservable(); + public keysToColors: Array<[string, string]>; + public colorsSaturationWeight = 0.5; /** * This method generates a determistic color from the given key, a list of [key, color] and a saturation weight. * - First the method checks if the [key,color] is defined in externalkeysToColors and returns the correspondant color. @@ -135,12 +137,11 @@ export class AwcColorGeneratorLoader extends ColorGeneratorLoader { } // int to rgb let hex = (hash & 0x00FFFFFF).toString(16).toUpperCase(); - hex = '00000'.substring(0, 6 - hex.length) + hex; + hex = '00000'.substring(0, 6 - hex.length) + hex; const color = mix(hex, hex); color.saturate(color.toHsv().s * saturationWeight + ((1 - saturationWeight) * 100)); return color.toHexString(); } - } export class SelectFormControl extends UntypedFormControl { diff --git a/projects/arlas-components/src/lib/components/donut/donut.component.spec.ts b/projects/arlas-components/src/lib/components/donut/donut.component.spec.ts index 969b46f1..8fecb073 100644 --- a/projects/arlas-components/src/lib/components/donut/donut.component.spec.ts +++ b/projects/arlas-components/src/lib/components/donut/donut.component.spec.ts @@ -21,8 +21,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { DonutComponent } from './donut.component'; import { ArlasColorService } from '../../services/color.generator.service'; -import { ColorGeneratorLoader } from '../componentsUtils'; +import { AwcColorGeneratorLoader, ColorGeneratorLoader } from '../componentsUtils'; import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core'; +import { ColorGeneratorModule } from '../../services/color.generator.module'; describe('DonutComponent', () => { let component: DonutComponent; @@ -33,11 +34,15 @@ describe('DonutComponent', () => { declarations: [ DonutComponent ], imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }), + ColorGeneratorModule.forRoot({ + loader: { + provide: ColorGeneratorLoader, + useClass: AwcColorGeneratorLoader + } + }) ], providers: [ - ArlasColorService, - ColorGeneratorLoader - ] + ArlasColorService ] }) .compileComponents(); })); diff --git a/projects/arlas-components/src/lib/components/donut/donut.component.ts b/projects/arlas-components/src/lib/components/donut/donut.component.ts index d5aa4d3d..dfb5476c 100644 --- a/projects/arlas-components/src/lib/components/donut/donut.component.ts +++ b/projects/arlas-components/src/lib/components/donut/donut.component.ts @@ -131,6 +131,11 @@ export class DonutComponent implements OnChanges { .subscribe((event: Event) => { this.donut.resize(this.el.nativeElement.childNodes[0]); }); + this.colorService.changekeysToColors$.subscribe(() => { + this.donut.donutParams.keysToColors = this.colorService.colorGenerator.keysToColors; + this.donut.donutParams.donutNodeColorizer = this.colorService; + this.donut.resize(this.el.nativeElement.childNodes[0]); + }); } public ngOnChanges(changes: SimpleChanges): void { diff --git a/projects/arlas-components/src/lib/components/histogram/histogram.component.spec.ts b/projects/arlas-components/src/lib/components/histogram/histogram.component.spec.ts index 442a44f7..fdc25512 100644 --- a/projects/arlas-components/src/lib/components/histogram/histogram.component.spec.ts +++ b/projects/arlas-components/src/lib/components/histogram/histogram.component.spec.ts @@ -21,9 +21,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { HistogramComponent } from './histogram.component'; import { ArlasColorService } from '../../services/color.generator.service'; -import { ColorGeneratorLoader } from '../componentsUtils'; +import { AwcColorGeneratorLoader, ColorGeneratorLoader } from '../componentsUtils'; import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { ColorGeneratorModule } from '../../services/color.generator.module'; describe('HistogramComponent', () => { let component: HistogramComponent; @@ -31,14 +32,19 @@ describe('HistogramComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HistogramComponent ], + declarations: [HistogramComponent], imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }), - MatTooltipModule + MatTooltipModule, + ColorGeneratorModule.forRoot({ + loader: { + provide: ColorGeneratorLoader, + useClass: AwcColorGeneratorLoader + } + }) ], providers: [ - ArlasColorService, - ColorGeneratorLoader + ArlasColorService ] }) .compileComponents(); diff --git a/projects/arlas-components/src/lib/components/mapgl-legend/mapgl-legend.component.spec.ts b/projects/arlas-components/src/lib/components/mapgl-legend/mapgl-legend.component.spec.ts index c0336ac3..a73eb7ab 100644 --- a/projects/arlas-components/src/lib/components/mapgl-legend/mapgl-legend.component.spec.ts +++ b/projects/arlas-components/src/lib/components/mapgl-legend/mapgl-legend.component.spec.ts @@ -7,10 +7,11 @@ import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core'; import { ArlasColorService } from '../../services/color.generator.service'; -import { ColorGeneratorLoader } from '../componentsUtils'; +import { AwcColorGeneratorLoader, ColorGeneratorLoader } from '../componentsUtils'; import { LayerIdToName } from './layer-name.pipe'; import { MapglLayerIconModule } from '../mapgl-layer-icon/mapgl-layer-icon.module'; import { MatMenuModule } from '@angular/material/menu'; +import { ColorGeneratorModule } from '../../services/color.generator.module'; describe('MapglLegendComponent', () => { let component: MapglLegendComponent; @@ -25,11 +26,16 @@ describe('MapglLegendComponent', () => { MatMenuModule, MatTooltipModule, MapglLayerIconModule, - TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }) + TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }), + ColorGeneratorModule.forRoot({ + loader: { + provide: ColorGeneratorLoader, + useClass: AwcColorGeneratorLoader + } + }) ], providers: [ - ArlasColorService, - ColorGeneratorLoader + ArlasColorService ] }) .compileComponents(); diff --git a/projects/arlas-components/src/lib/components/powerbars/powerbars.component.spec.ts b/projects/arlas-components/src/lib/components/powerbars/powerbars.component.spec.ts index 2e74229c..cb40a756 100644 --- a/projects/arlas-components/src/lib/components/powerbars/powerbars.component.spec.ts +++ b/projects/arlas-components/src/lib/components/powerbars/powerbars.component.spec.ts @@ -21,13 +21,14 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { PowerbarsComponent } from './powerbars.component'; import { ArlasColorService } from '../../services/color.generator.service'; -import { ColorGeneratorLoader } from '../componentsUtils'; +import { AwcColorGeneratorLoader, ColorGeneratorLoader } from '../componentsUtils'; import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core'; import { FormatNumberPipe } from '../../pipes/format-number/format-number.pipe'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatInputModule } from '@angular/material/input'; +import { ColorGeneratorModule } from '../../services/color.generator.module'; describe('PowerbarsComponent', () => { let component: PowerbarsComponent; @@ -42,10 +43,15 @@ describe('PowerbarsComponent', () => { MatIconModule, MatTooltipModule, MatInputModule, + ColorGeneratorModule.forRoot({ + loader: { + provide: ColorGeneratorLoader, + useClass: AwcColorGeneratorLoader + } + }) ], providers: [ - ArlasColorService, - ColorGeneratorLoader + ArlasColorService ] }) .compileComponents(); diff --git a/projects/arlas-components/src/lib/components/powerbars/powerbars.component.ts b/projects/arlas-components/src/lib/components/powerbars/powerbars.component.ts index b26d098b..d22c7b70 100644 --- a/projects/arlas-components/src/lib/components/powerbars/powerbars.component.ts +++ b/projects/arlas-components/src/lib/components/powerbars/powerbars.component.ts @@ -194,6 +194,15 @@ export class PowerbarsComponent implements OnInit, OnChanges, AfterViewInit { public NUMBER_FORMAT_CHAR = NUMBER_FORMAT_CHAR; public constructor(private colorService: ArlasColorService) { + this.colorService.changekeysToColors$.subscribe(() => { + this.powerBarsList.forEach(p => { + if (this.useColorService) { + const rgbaColor = tinycolor.default(this.colorService.getColor(p.term, this.keysToColors, + this.colorsSaturationWeight)).toRgb(); + p.color = this.getPowerbarColor(rgbaColor); + } + }); + }); } public static getPowerbarsJsonSchema(): Object { @@ -216,7 +225,7 @@ export class PowerbarsComponent implements OnInit, OnChanges, AfterViewInit { if (this.useColorService) { const rgbaColor = tinycolor.default(this.colorService.getColor(missingLeafToUpdate.term, this.keysToColors, this.colorsSaturationWeight)).toRgb(); - missingLeafToUpdate.color = 'rgba(' + [rgbaColor.r, rgbaColor.g, rgbaColor.b, 0.7].join(',') + ')'; + missingLeafToUpdate.color = this.getPowerbarColor(rgbaColor); } this.selectedPowerbarsSet.delete(missingLeaf); this.selectedPowerbarsSet.add(missingLeafToUpdate); @@ -309,11 +318,9 @@ export class PowerbarsComponent implements OnInit, OnChanges, AfterViewInit { if (this.useColorService) { const rgbaColor = tinycolor.default(this.colorService.getColor(powerBar.term, this.keysToColors, this.colorsSaturationWeight)).toRgb(); - powerBar.color = 'rgba(' + [rgbaColor.r, rgbaColor.g, rgbaColor.b, 0.7].join(',') + ')'; + powerBar.color = this.getPowerbarColor(rgbaColor); } } else { - - powerBar = currentPath.length > 1 ? new PowerBar(currentPath[0].fieldValue, currentPath[1].fieldValue, 0) : new PowerBar(currentPath[0].fieldValue, 'root', 0); powerBar.path = currentPath; @@ -387,7 +394,7 @@ export class PowerbarsComponent implements OnInit, OnChanges, AfterViewInit { if (this.useColorService) { const rgbaColor = tinycolor.default(this.colorService.getColor(powerBar.term, this.keysToColors, this.colorsSaturationWeight)).toRgb(); - powerBar.color = 'rgba(' + [rgbaColor.r, rgbaColor.g, rgbaColor.b, 0.7].join(',') + ')'; + powerBar.color = this.getPowerbarColor(rgbaColor); } if (this.useColorFromData) { powerBar.color = child.color.toString()[0] === '#' ? child.color.toString() : '#'.concat(child.color.toString()); @@ -490,4 +497,8 @@ export class PowerbarsComponent implements OnInit, OnChanges, AfterViewInit { }); return foundPowerbar; } + + private getPowerbarColor(rgbaColor: tinycolor.ColorFormats.RGBA): string{ + return 'rgba(' + [rgbaColor.r, rgbaColor.g, rgbaColor.b, 0.7].join(',') + ')'; + } } diff --git a/projects/arlas-components/src/lib/components/results/result-item/result-item.component.html b/projects/arlas-components/src/lib/components/results/result-item/result-item.component.html index 654b4247..0e9bf14a 100644 --- a/projects/arlas-components/src/lib/components/results/result-item/result-item.component.html +++ b/projects/arlas-components/src/lib/components/results/result-item/result-item.component.html @@ -17,11 +17,12 @@ diff --git a/projects/arlas-components/src/lib/components/results/result-item/result-item.component.spec.ts b/projects/arlas-components/src/lib/components/results/result-item/result-item.component.spec.ts index 8652ac30..5918d1a7 100644 --- a/projects/arlas-components/src/lib/components/results/result-item/result-item.component.spec.ts +++ b/projects/arlas-components/src/lib/components/results/result-item/result-item.component.spec.ts @@ -21,10 +21,12 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ResultItemComponent } from './result-item.component'; import { ArlasColorService } from '../../../services/color.generator.service'; -import { ColorGeneratorLoader } from '../../componentsUtils'; +import { AwcColorGeneratorLoader, ColorGeneratorLoader } from '../../componentsUtils'; import { TranslateFakeLoader, TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { ColorGeneratorModule } from '../../../services/color.generator.module'; +import { Item } from '../model/item'; describe('ResultItemComponent', () => { let component: ResultItemComponent; @@ -36,10 +38,15 @@ describe('ResultItemComponent', () => { imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }), MatIconModule, - MatTooltipModule + MatTooltipModule, + ColorGeneratorModule.forRoot({ + loader: { + provide: ColorGeneratorLoader, + useClass: AwcColorGeneratorLoader + } + }) ], providers: [ - ColorGeneratorLoader, ArlasColorService ] }) diff --git a/projects/arlas-components/src/lib/components/results/result-item/result-item.component.ts b/projects/arlas-components/src/lib/components/results/result-item/result-item.component.ts index 60365905..be3a186d 100644 --- a/projects/arlas-components/src/lib/components/results/result-item/result-item.component.ts +++ b/projects/arlas-components/src/lib/components/results/result-item/result-item.component.ts @@ -126,16 +126,20 @@ export class ResultItemComponent extends ItemComponent implements OnInit { public detailedData = ''; public actions; public borderStyle = 'solid'; + public colors = {}; protected identifier: string; public NUMBER_FORMAT_CHAR = NUMBER_FORMAT_CHAR; public constructor(public colorService: ArlasColorService, public translate: TranslateService) { super(); + this.colorService.changekeysToColors$.subscribe(() => this.updateColors()); } public ngOnInit() { this.identifier = this.rowItem?.identifier; + this.updateColors(); + } // Detailed data is retrieved wheb the row is toggled for the first time @@ -166,24 +170,42 @@ export class ResultItemComponent extends ItemComponent implements OnInit { this.selectedItemsEvent.next(this.selectedItems); } - public getColor(key): string { + public getTextColor(key): string { if (key !== undefined && key !== null) { - return this.colorService.getColor(key.toString(), this.keysToColors, this.colorsSaturationWeight); + return this.colorService.getTextColor(key.toString()); } else { return ''; } } - public getTextColor(key: string): string { + public triggerActionOnItem(action: Action) { + this.actionOnItemEvent.next({ action: action, elementidentifier: { idFieldName: this.idFieldName, idValue: this.rowItem.identifier } }); + } + + private updateColors() { + const newColor = {}; + this.rowItem?.columns.forEach(c => { + if(c.useColorService){ + const key = this.rowItem?.itemData.get(c.fieldName); + if (key !== undefined && key !== null) { + newColor[key.toString()] = {}; + newColor[key.toString()]['color'] = this.getColor(key); + newColor[key.toString()]['textColor'] = this.getTextColor(key); + + } + } + }); + this.colors = newColor; + } + + private getColor(key): string { if (key !== undefined && key !== null) { - return this.colorService.getTextColor(key.toString()); + return this.colorService.getColor(key.toString(), this.keysToColors, this.colorsSaturationWeight); } else { return ''; } } - public triggerActionOnItem(action: Action) { - this.actionOnItemEvent.next({ action: action, elementidentifier: { idFieldName: this.idFieldName, idValue: this.rowItem.identifier } }); - } + } diff --git a/projects/arlas-components/src/lib/components/results/result-list/result-list.component.spec.ts b/projects/arlas-components/src/lib/components/results/result-list/result-list.component.spec.ts index 791422aa..aef0c4ef 100644 --- a/projects/arlas-components/src/lib/components/results/result-list/result-list.component.spec.ts +++ b/projects/arlas-components/src/lib/components/results/result-list/result-list.component.spec.ts @@ -21,7 +21,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ResultListComponent } from './result-list.component'; import { ArlasColorService } from '../../../services/color.generator.service'; -import { ColorGeneratorLoader } from '../../componentsUtils'; +import { AwcColorGeneratorLoader, ColorGeneratorLoader } from '../../componentsUtils'; import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core'; import { MatMenuModule } from '@angular/material/menu'; import { MatGridListModule } from '@angular/material/grid-list'; @@ -31,6 +31,7 @@ import { MatOptionModule } from '@angular/material/core'; import { ResultScrollDirective } from '../result-directive/result-scroll.directive'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { FormsModule } from '@angular/forms'; +import { ColorGeneratorModule } from '../../../services/color.generator.module'; describe('ResultListComponent', () => { let component: ResultListComponent; @@ -38,10 +39,9 @@ describe('ResultListComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ResultListComponent, ResultScrollDirective ], + declarations: [ResultListComponent, ResultScrollDirective], providers: [ - ArlasColorService, - ColorGeneratorLoader + ArlasColorService ], imports: [ TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }), @@ -51,7 +51,13 @@ describe('ResultListComponent', () => { MatSelectModule, MatOptionModule, MatCheckboxModule, - FormsModule + FormsModule, + ColorGeneratorModule.forRoot({ + loader: { + provide: ColorGeneratorLoader, + useClass: AwcColorGeneratorLoader + } + }) ] }) .compileComponents(); diff --git a/projects/arlas-components/src/lib/services/color.generator.service.ts b/projects/arlas-components/src/lib/services/color.generator.service.ts index 97c42c71..974ffe1c 100644 --- a/projects/arlas-components/src/lib/services/color.generator.service.ts +++ b/projects/arlas-components/src/lib/services/color.generator.service.ts @@ -19,11 +19,17 @@ import { Injectable } from '@angular/core'; import { ColorGeneratorLoader } from '../components/componentsUtils'; +import { Subject } from 'rxjs'; @Injectable() export class ArlasColorService { - public constructor(public colorGenerator: ColorGeneratorLoader) {} + private changekeysToColors = new Subject(); + public changekeysToColors$ = this.changekeysToColors.asObservable(); + public constructor(public colorGenerator: ColorGeneratorLoader) { + this.colorGenerator.changekeysToColors$.subscribe(() => this.changekeysToColors.next()); + + } public getColor(key: string, keysToColors?: Array<[string, string]>, colorsSaturationWeight?: number): string { return this.colorGenerator.getColor(key, keysToColors, colorsSaturationWeight); @@ -32,6 +38,7 @@ export class ArlasColorService { public getTextColor(color): string { return this.colorGenerator.getTextColor(color); } + }