chromium/chrome/browser/resources/side_panel/customize_chrome/cards.ts

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

import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
import 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
import 'chrome://resources/cr_elements/policy/cr_policy_indicator.js';
import './strings.m.js';

import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';

import {getCss} from './cards.css.js';
import {getHtml} from './cards.html.js';
import {CustomizeChromeAction, recordCustomizeChromeAction} from './common.js';
import type {CustomizeChromePageHandlerInterface, ModuleSettings} from './customize_chrome.mojom-webui.js';
import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';

export interface CardsElement {
  $: {
    showToggleContainer: HTMLElement,
  };
}

/*
 * Element that lets the user configure module status and settings. From a UI
 * standpoint, we refer to modules as cards.
 */
export class CardsElement extends CrLitElement {
  static get is() {
    return 'customize-chrome-cards';
  }

  static override get styles() {
    return getCss();
  }

  override render() {
    return getHtml.bind(this)();
  }

  static override get properties() {
    return {
      /** The list of modules that can be enabled or disabled on the NTP. */
      modules_: {type: Array},

      /** Whether the modules are customizable or not. */
      show_: {type: Boolean},

      /** Whether the modules are managed by admin policies or not. */
      managedByPolicy_: {type: Boolean},

      initialized_: {type: Boolean},
    };
  }

  protected modules_: ModuleSettings[] = [];
  protected show_: boolean = false;
  protected managedByPolicy_: boolean = false;
  private pageHandler_: CustomizeChromePageHandlerInterface;
  private setModulesSettingsListenerId_: number|null = null;
  protected initialized_: boolean = false;

  constructor() {
    super();
    this.pageHandler_ = CustomizeChromeApiProxy.getInstance().handler;
  }

  override connectedCallback() {
    super.connectedCallback();
    this.setModulesSettingsListenerId_ =
        CustomizeChromeApiProxy.getInstance()
            .callbackRouter.setModulesSettings.addListener(
                (modulesSettings: ModuleSettings[], managed: boolean,
                 visible: boolean) => {
                  this.show_ = visible;
                  this.managedByPolicy_ = managed;
                  this.modules_ = modulesSettings;
                  this.initialized_ = true;
                });
    this.pageHandler_.updateModulesSettings();
  }

  override disconnectedCallback() {
    super.disconnectedCallback();
    CustomizeChromeApiProxy.getInstance().callbackRouter.removeListener(
        this.setModulesSettingsListenerId_!);
  }

  private setShow_(show: boolean) {
    recordCustomizeChromeAction(
        CustomizeChromeAction.SHOW_CARDS_TOGGLE_CLICKED);
    this.show_ = show;
    this.pageHandler_.setModulesVisible(this.show_);
  }

  protected onShowChange_(e: CustomEvent<boolean>) {
    this.setShow_(e.detail);
  }

  protected onShowToggleClick_() {
    if (this.managedByPolicy_) {
      return;
    }

    this.setShow_(!this.show_);
  }

  private setModuleStatus(index: number, enabled: boolean) {
    const module = this.modules_[index]!;
    module.enabled = enabled;
    this.requestUpdate();
    const id = module.id;
    this.pageHandler_.setModuleDisabled(id, !enabled);
    const metricBase = `NewTabPage.Modules.${enabled ? 'Enabled' : 'Disabled'}`;
    chrome.metricsPrivate.recordSparseValueWithPersistentHash(metricBase, id);
    chrome.metricsPrivate.recordSparseValueWithPersistentHash(
        `${metricBase}.Customize`, id);
  }

  protected onCardCheckboxChange_(e: CustomEvent<boolean>) {
    const index = Number((e.currentTarget as HTMLElement).dataset['index']);
    this.setModuleStatus(index, /*checked= */ e.detail);
  }

  protected onCardClick_(e: Event) {
    if (this.managedByPolicy_) {
      return;
    }

    const index = Number((e.currentTarget as HTMLElement).dataset['index']);
    const module = this.modules_[index]!;
    this.setModuleStatus(index, !module.enabled);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'customize-chrome-cards': CardsElement;
  }
}

customElements.define(CardsElement.is, CardsElement);