All files / avatar si-avatar-background-color.directive.ts

95.45% Statements 21/22
75% Branches 6/8
100% Functions 3/3
95.23% Lines 20/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80                            1x 1x 1x                     1x               24x               24x   24x     13x 13x               15x 13x     2x 2x 4x 4x   2x       15x     15x 15x 15x        
/**
 * Copyright (c) Siemens 2016 - 2025
 * SPDX-License-Identifier: MIT
 */
import {
  booleanAttribute,
  Directive,
  input,
  numberAttribute,
  OnChanges,
  signal,
  SimpleChanges
} from '@angular/core';
 
const DATA_COLORS_MAX = 17;
const DATA_COLOR_NEUTRAL = 17;
const ASCII_CODE_INDEX_A = 64;
 
/**
 * The directive provide a CSS variable --background with a color based on the initials or alt text.
 */
@Directive({
  selector: '[siAvatarBackgroundColor]',
  host: {
    '[style.--background]': 'backgroundStyle()'
  }
})
export class SiAvatarBackgroundColorDirective implements OnChanges {
  /**
   * The desired color index from $element-data-* color tokens. This can be set to any kind of
   * positive integer that is then mapped to a color index.
   * A better way to set a pseudo-random color is to set {@link autoColor} to `true`.
   *
   * @defaultValue undefined
   */
  readonly color = input<number | undefined, unknown>(undefined, { transform: numberAttribute });
 
  /**
   * Automatically calculates the background color.
   * If set, {@link color} will be ignored.
   *
   * @defaultValue false
   */
  readonly autoColor = input(false, { transform: booleanAttribute });
 
  protected readonly backgroundStyle = signal<string | undefined>('var(--element-data-17)');
 
  ngOnChanges(changes: SimpleChanges): void {
    if (changes.color) {
      this.setColor(this.color());
    }
  }
 
  /**
   * Update background color variable based on the initials or placeholder text.
   */
  public calculateColorFromInitials(displayInitials?: string): void {
    if (!this.autoColor() || !displayInitials) {
      return;
    }
 
    let color = 0;
    for (let i = 0; i < displayInitials.length; i++) {
      color *= 17; // this prevents 'JD' to have the same color as 'DJ'
      color += displayInitials.charCodeAt(i) - ASCII_CODE_INDEX_A;
    }
    this.setColor(color);
  }
 
  private setColor(color?: number): void {
    Iif (this.color() === 0) {
      this.backgroundStyle.set(undefined);
    } else {
      const actualColor = color ?? DATA_COLOR_NEUTRAL;
      const colorIndex = ((actualColor - 1) % DATA_COLORS_MAX) + 1;
      this.backgroundStyle.set(`var(--element-data-${colorIndex})`);
    }
  }
}