chromium/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts

// Copyright 2018 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_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import './advanced_settings_item.js';
import './print_preview_search_box.js';
import './print_preview_shared.css.js';
import './print_preview_vars.css.js';
import '../strings.m.js';

import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {removeHighlights} from 'chrome://resources/js/search_highlight_utils.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import type {Destination} from '../data/destination.js';
import {MetricsContext, PrintSettingsUiBucket} from '../metrics.js';

import {getTemplate} from './advanced_settings_dialog.html.js';
import type {PrintPreviewSearchBoxElement} from './print_preview_search_box.js';
import {SettingsMixin} from './settings_mixin.js';

export interface PrintPreviewAdvancedSettingsDialogElement {
  $: {
    dialog: CrDialogElement,
    searchBox: PrintPreviewSearchBoxElement,
  };
}

const PrintPreviewAdvancedSettingsDialogElementBase =
    I18nMixin(SettingsMixin(PolymerElement));

export class PrintPreviewAdvancedSettingsDialogElement extends
    PrintPreviewAdvancedSettingsDialogElementBase {
  static get is() {
    return 'print-preview-advanced-settings-dialog';
  }

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

  static get properties() {
    return {
      destination: Object,

      searchQuery_: {
        type: Object,
        value: null,
      },

      hasMatching_: {
        type: Boolean,
        notify: true,
        computed: 'computeHasMatching_(searchQuery_)',
      },
    };
  }

  destination: Destination;
  private searchQuery_: RegExp|null;
  private hasMatching_: boolean;
  private highlights_: HTMLElement[] = [];
  private bubbles_: Map<HTMLElement, number> = new Map();
  private metrics_: MetricsContext = MetricsContext.printSettingsUi();

  override ready() {
    super.ready();

    this.addEventListener('keydown', e => this.onKeydown_(e as KeyboardEvent));
  }

  override connectedCallback() {
    super.connectedCallback();

    this.metrics_.record(PrintSettingsUiBucket.ADVANCED_SETTINGS_DIALOG_SHOWN);
    this.$.dialog.showModal();
  }

  private onKeydown_(e: KeyboardEvent) {
    e.stopPropagation();
    const searchInput = this.$.searchBox.getSearchInput();
    const eventInSearchBox = e.composedPath().includes(searchInput);
    if (e.key === 'Escape' &&
        (!eventInSearchBox || !searchInput.value.trim())) {
      this.$.dialog.cancel();
      e.preventDefault();
      return;
    }

    if (e.key === 'Enter' && !eventInSearchBox) {
      const activeElementTag = (e.composedPath()[0] as HTMLElement).tagName;
      if (['CR-BUTTON', 'SELECT'].includes(activeElementTag)) {
        return;
      }
      this.onApplyButtonClick_();
      e.preventDefault();
      return;
    }
  }

  /**
   * @return Whether there is more than one vendor item to display.
   */
  private hasMultipleItems_(): boolean {
    return this.destination.capabilities!.printer.vendor_capability!.length > 1;
  }

  /**
   * @return Whether there is a setting matching the query.
   */
  private computeHasMatching_(): boolean {
    if (!this.shadowRoot) {
      return true;
    }

    removeHighlights(this.highlights_);
    this.bubbles_.forEach((_number, bubble) => bubble.remove());
    this.highlights_ = [];
    this.bubbles_.clear();

    const listItems = this.shadowRoot!.querySelectorAll(
        'print-preview-advanced-settings-item');
    let hasMatch = false;
    listItems.forEach(item => {
      const matches = item.hasMatch(this.searchQuery_);
      item.hidden = !matches;
      hasMatch = hasMatch || matches;
      this.highlights_.push(
          ...item.updateHighlighting(this.searchQuery_, this.bubbles_));
    });
    return hasMatch;
  }

  /**
   * @return Whether the no matching settings hint should be shown.
   */
  private shouldShowHint_(): boolean {
    return !!this.searchQuery_ && !this.hasMatching_;
  }

  private onCloseOrCancel_() {
    if (this.searchQuery_) {
      this.$.searchBox.setValue('');
    }
    if (this.$.dialog.getNative().returnValue === 'success') {
      this.metrics_.record(
          PrintSettingsUiBucket.ADVANCED_SETTINGS_DIALOG_CANCELED);
    }
  }

  private onCancelButtonClick_() {
    this.$.dialog.cancel();
  }

  private onApplyButtonClick_() {
    const settingsValues: {[settingName: string]: any} = {};
    const itemList = this.shadowRoot!.querySelectorAll(
        'print-preview-advanced-settings-item');
    itemList.forEach(item => {
      settingsValues[item.capability.id] = item.getCurrentValue();
    });
    this.setSetting('vendorItems', settingsValues);
    this.$.dialog.close();
  }

  close() {
    this.$.dialog.close();
  }

  private isSearching_(): string {
    return this.searchQuery_ ? 'searching' : '';
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'print-preview-advanced-settings-dialog':
        PrintPreviewAdvancedSettingsDialogElement;
  }
}

customElements.define(
    PrintPreviewAdvancedSettingsDialogElement.is,
    PrintPreviewAdvancedSettingsDialogElement);