chromium/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.ts

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview
 * The color icon indicates wallpaper or preset colors in keyboard backlight and
 * zone customization section.
 */

import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';

import {WithPersonalizationStore} from '../personalization_store.js';
import {convertToRgbHexStr, getPresetColors, GREEN, INDIGO, RAINBOW, RED, WALLPAPER, YELLOW} from '../utils.js';

import {getTemplate} from './color_icon_element.html.js';

/**
  Based on this algorithm suggested by the W3:
  https://www.w3.org/TR/AERT/#color-contrast
*/
function calculateColorBrightness(hexVal: number): number {
  const r = (hexVal >> 16) & 0xff;  // extract red
  const g = (hexVal >> 8) & 0xff;   // extract green
  const b = (hexVal >> 0) & 0xff;   // extract blue
  return (r * 299 + g * 587 + b * 114) / 1000;
}

export class ColorIconElement extends WithPersonalizationStore {
  static get is() {
    return 'color-icon';
  }

  static get template() {
    return getTemplate();
  }

  static get properties() {
    return {
      /** The color id indicates the color of the icon. */
      colorId: {
        type: String,
        value: null,
        reflectToAttribute: true,
      },

      /** The current wallpaper extracted color. */
      wallpaperColor_: Object,
    };
  }

  colorId: string|null;
  private wallpaperColor_: SkColor|null;

  override connectedCallback() {
    super.connectedCallback();
    this.watch(
        'wallpaperColor_', state => state.keyboardBacklight.wallpaperColor);
    this.updateFromStore();
  }

  private isWallpaperColorId_(colorId: string|null): boolean {
    return colorId === WALLPAPER;
  }

  private getColorInnerContainerStyle_(colorId: string|null): string {
    if (!colorId) {
      return '';
    }
    const colors = getPresetColors();
    const outlineStyle = `outline: 2px solid var(--cros-separator-color);
                    outline-offset: -2px;`;
    switch (colorId) {
      case RAINBOW:
        return `background-image: linear-gradient(90deg,
            ${colors[RED].hexVal},
            ${colors[YELLOW].hexVal},
            ${colors[GREEN].hexVal},
            ${colors[INDIGO].hexVal});
            ${outlineStyle}`;
      default:
        return `background-color: ${colors[colorId].hexVal};
                                    ${outlineStyle};`;
    }
  }

  private getWallpaperColorInnerContainerStyle_(wallpaperColor: SkColor|
                                                null): string {
    const hexStr = !wallpaperColor ?
        '#FFFFFF' :
        convertToRgbHexStr(wallpaperColor.value & 0xFFFFFF);
    return `background-color: ${hexStr};
        outline: 2px solid var(--cros-separator-color);
        outline-offset: -2px;`;
  }

  private getWallpaperIconColorClass_(wallpaperColor: SkColor): string {
    if (!wallpaperColor || (wallpaperColor.value & 0xFFFFFF) === 0xFFFFFF) {
      return `light-icon`;
    }
    const brightness =
        calculateColorBrightness(wallpaperColor.value & 0xFFFFFF);
    if (brightness < 125) {
      return `dark-icon`;
    }
    return `light-icon`;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'color-icon': ColorIconElement;
  }
}

customElements.define(ColorIconElement.is, ColorIconElement);