chromium/chrome/browser/resources/chromeos/healthd_internals/line_chart/menu.ts

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

import '//resources/ash/common/cr_elements/cr_button/cr_button.js';

import {assert} from '//resources/js/assert.js';
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {MENU_TEXT_COLOR_DARK, MENU_TEXT_COLOR_LIGHT} from './configs.js';
import {getTemplate} from './menu.html.js';
import {DataSeries} from './utils/data_series.js';

/**
 * Creates an element of a specified type with a specified class name.
 */
function createElementWithClassName(
    type: string, className: string): HTMLElement {
  const element = document.createElement(type);
  element.className = className;
  return element;
}

export interface HealthdInternalsLineChartMenuElement {
  $: {
    dataButtonsContainer: HTMLElement,
  };
}

/**
 * A menu as a button container to control the visibility of each `DataSeries`
 * in current line chart.
 */
export class HealthdInternalsLineChartMenuElement extends PolymerElement {
  static get is() {
    return 'healthd-internals-line-chart-menu';
  }

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

  // Buttons of data series.
  private displayedButtons: HTMLElement[] = [];

  // Used to display buttons for changing visibility of each data.
  private dataSeries: DataSeries[] = [];

  getWidth(): number {
    return this.$.dataButtonsContainer.offsetWidth;
  }

  /**
   * Add a data series to the menu.
   */
  addDataSeries(dataSeries: DataSeries) {
    const idx: number = this.dataSeries.indexOf(dataSeries);
    if (idx !== -1) {
      return;
    }
    const button: HTMLElement = this.createButton(dataSeries);
    this.$.dataButtonsContainer.appendChild(button);
    this.dataSeries.push(dataSeries);
    this.displayedButtons.push(button);

    this.fireMenuButtonsUpdatedEvent();
  }

  private fireMenuButtonsUpdatedEvent() {
    this.dispatchEvent(new CustomEvent(
        'menu-buttons-updated', {bubbles: true, composed: true}));
  }

  /**
   * Create a button to control the data series.
   */
  private createButton(dataSeries: DataSeries): HTMLElement {
    const buttonInner: HTMLElement =
        createElementWithClassName('span', 'line-chart-menu-button-inner-span');
    buttonInner.innerText = dataSeries.getTitle();
    const button: HTMLElement =
        createElementWithClassName('div', 'line-chart-menu-button');
    button.appendChild(buttonInner);
    this.setupButtonOnClickHandler(button, dataSeries);
    this.updateButtonStyle(button, dataSeries);
    return button;
  }

  /**
   * Add a onclick handler to the button.
   */
  private setupButtonOnClickHandler(
      button: HTMLElement, dataSeries: DataSeries) {
    button.addEventListener('click', () => {
      dataSeries.setVisible(!dataSeries.getVisible());
      this.updateButtonStyle(button, dataSeries);
      this.fireMenuButtonsUpdatedEvent();
    });
  }

  /**
   * Update the button style with the visibility and color from data series.
   */
  private updateButtonStyle(button: HTMLElement, dataSeries: DataSeries) {
    if (dataSeries.getVisible()) {
      button.style.backgroundColor = dataSeries.getColor();
      button.style.color = MENU_TEXT_COLOR_LIGHT;
    } else {
      button.style.backgroundColor = MENU_TEXT_COLOR_LIGHT;
      button.style.color = MENU_TEXT_COLOR_DARK;
    }
  }

  private onEnableAllButtonClick() {
    assert(this.dataSeries.length === this.displayedButtons.length);
    for (let i: number = 0; i < this.dataSeries.length; ++i) {
      this.dataSeries[i].setVisible(true);
      this.updateButtonStyle(this.displayedButtons[i], this.dataSeries[i]);
    }
    this.fireMenuButtonsUpdatedEvent();
  }

  private onDisableAllButtonClicked() {
    assert(this.dataSeries.length === this.displayedButtons.length);
    for (let i: number = 0; i < this.dataSeries.length; ++i) {
      this.dataSeries[i].setVisible(false);
      this.updateButtonStyle(this.displayedButtons[i], this.dataSeries[i]);
    }
    this.fireMenuButtonsUpdatedEvent();
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'healthd-internals-line-chart-menu': HealthdInternalsLineChartMenuElement;
  }
}

customElements.define(
    HealthdInternalsLineChartMenuElement.is,
    HealthdInternalsLineChartMenuElement);