chromium/chrome/browser/resources/settings/autofill_page/payments_list.ts

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

/**
 * @fileoverview 'payments-list' is a list of saved payment methods (credit
 * cards etc.) to be shown in the settings page.
 */

import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import '../settings_shared.css.js';
import './credit_card_list_entry.js';
import './iban_list_entry.js';
import './passwords_shared.css.js';

import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {loadTimeData} from '../i18n_setup.js';

import type {SettingsCreditCardListEntryElement} from './credit_card_list_entry.js';
import type {SettingsIbanListEntryElement} from './iban_list_entry.js';
import {getTemplate} from './payments_list.html.js';

export class SettingsPaymentsListElement extends PolymerElement {
  static get is() {
    return 'settings-payments-list';
  }

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

  static get properties() {
    return {
      /**
       * An array of all saved credit cards.
       */
      creditCards: Array,

      /**
       * An array of all saved IBANs.
       */
      ibans: Array,

      /**
       * True if displaying IBANs in settings is enabled.
       */
      enableIbans_: {
        type: Boolean,
        value() {
          return loadTimeData.getBoolean('showIbansSettings');
        },
      },

      /**
       * True iff both credit cards and IBANs will be shown.
       */
      showCreditCardIbanSeparator_: {
        type: Boolean,
        value: false,
        computed: 'computeShowCreditCardIbanSeparator_(' +
            'creditCards, ibans, enableIbans_)',
      },

      /**
       * True iff any payment methods will be shown.
       */
      showAnyPaymentMethods_: {
        type: Boolean,
        value: false,
        computed: 'computeShowAnyPaymentMethods_(' +
            'creditCards, ibans, enableIbans_)',
      },
    };
  }

  creditCards: chrome.autofillPrivate.CreditCardEntry[];
  ibans: chrome.autofillPrivate.IbanEntry[];
  private enableIbans_: boolean;
  private showCreditCardIbanSeparator_: boolean;
  private showAnyPaymentMethods_: boolean;

  /**
   * Focuses the next most appropriate element after removing a specific
   * credit card. Returns `false` if it could not find such an element,
   * in this case the focus is supposed to be handled by someone else.
   */
  updateFocusBeforeCreditCardRemoval(cardIndex: number): boolean {
    // The focused element is to be reset only if the last element is deleted,
    // when the number of "dom-repeat" nodes changes and the focus get lost.
    if (cardIndex === this.creditCards.length - 1) {
      return this.updateFocusBeforeRemoval_(this.getCreditCardId_(cardIndex));
    } else {
      return true;
    }
  }

  /**
   * Focuses the next most appropriate element after removing a specific
   * iban. Returns `false` if it could not find such an element,
   * in this case the focus is supposed to be handled by someone else.
   */
  updateFocusBeforeIbanRemoval(ibanIndex: number): boolean {
    // The focused element is to be reset only if the last element is deleted,
    // when the number of "dom-repeat" nodes changes and the focus get lost.
    if (ibanIndex === this.ibans.length - 1) {
      return this.updateFocusBeforeRemoval_(this.getIbanId_(ibanIndex));
    } else {
      return true;
    }
  }

  /**
   * Handles focus resetting across all payment method lists. Returns `false`
   * only when the last payment method is removed, in other cases sets the focus
   * to either the next or previous payment method.
   */
  private updateFocusBeforeRemoval_(id: string): boolean {
    const paymentMethods =
        this.shadowRoot!.querySelectorAll<SettingsCreditCardListEntryElement|
                                          SettingsIbanListEntryElement>(
            '.payment-method');

    if (paymentMethods.length <= 1) {
      return false;
    }

    const index = [...paymentMethods].findIndex((element) => element.id === id);
    const isLastItem = index === paymentMethods.length - 1;
    const indexToFocus = index + (isLastItem ? -1 : +1);
    const menu = paymentMethods[indexToFocus].dotsMenu;
    if (menu) {
      focusWithoutInk(menu);
      return true;
    }

    return false;
  }

  private getCreditCardId_(index: number): string {
    return `card-${index}`;
  }

  private getIbanId_(index: number): string {
    return `iban-${index}`;
  }

  /**
   * @return Whether the list exists and has items.
   */
  private hasSome_(list: any[]): boolean {
    return !!(list && list.length);
  }

  /**
   * @return true iff there are credit cards to be shown.
   */
  private showCreditCards_(): boolean {
    return this.hasSome_(this.creditCards);
  }

  /**
   * @return true iff there are IBANs to be shown.
   */
  private showIbans_(): boolean {
    return this.enableIbans_ && this.hasSome_(this.ibans);
  }

  /**
   * @return true iff both credit cards and IBANs will be shown.
   */
  private computeShowCreditCardIbanSeparator_(): boolean {
    return this.showCreditCards_() && this.showIbans_();
  }

  /**
   * @return true iff any payment methods will be shown.
   */
  private computeShowAnyPaymentMethods_(): boolean {
    return this.showCreditCards_() || this.showIbans_();
  }
}

customElements.define(
    SettingsPaymentsListElement.is, SettingsPaymentsListElement);