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

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

/**
 * Note: Chrome OS uses print-preview-destination-select-cros rather than the
 * element in this file. Ensure any fixes for cross platform bugs work on both
 * Chrome OS and non-Chrome OS.
 */

import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/cr_elements/md_select.css.js';
import 'chrome://resources/js/util.js';
import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
import './destination_select_style.css.js';
import './icons.html.js';
import './print_preview_shared.css.js';
import './throbber.css.js';
import '../strings.m.js';

import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {IronMeta} from 'chrome://resources/polymer/v3_0/iron-meta/iron-meta.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import type {Destination} from '../data/destination.js';
import {PDF_DESTINATION_KEY} from '../data/destination.js';
import {getSelectDropdownBackground} from '../print_preview_utils.js';

import {getTemplate} from './destination_select.html.js';
import {SelectMixin} from './select_mixin.js';

const PrintPreviewDestinationSelectElementBase =
    I18nMixin(SelectMixin(PolymerElement));

export class PrintPreviewDestinationSelectElement extends
    PrintPreviewDestinationSelectElementBase {
  static get is() {
    return 'print-preview-destination-select';
  }

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

  static get properties() {
    return {
      activeUser: String,

      dark: Boolean,

      destination: Object,

      disabled: Boolean,

      loaded: Boolean,

      noDestinations: Boolean,

      pdfPrinterDisabled: Boolean,

      recentDestinationList: Array,

      pdfDestinationKey_: {
        type: String,
        value: PDF_DESTINATION_KEY,
      },
    };
  }

  activeUser: string;
  dark: boolean;
  destination: Destination;
  disabled: boolean;
  loaded: boolean;
  noDestinations: boolean;
  pdfPrinterDisabled: boolean;
  recentDestinationList: Destination[];
  private pdfDestinationKey_: string;
  private meta_: IronMeta;

  constructor() {
    super();

    this.meta_ = new IronMeta({type: 'iconset', value: undefined});
  }

  override focus() {
    this.shadowRoot!.querySelector<HTMLElement>('.md-select')!.focus();
  }

  /** Sets the select to the current value of |destination|. */
  updateDestination() {
    this.selectedValue = this.destination.key;
  }

  /**
   * Returns the iconset and icon for the selected printer. If printer details
   * have not yet been retrieved from the backend, attempts to return an
   * appropriate icon early based on the printer's sticky information.
   * @return The iconset and icon for the current selection.
   */
  private getDestinationIcon_(): string {
    if (!this.selectedValue) {
      return '';
    }

    // If the destination matches the selected value, pull the icon from the
    // destination.
    if (this.destination && this.destination.key === this.selectedValue) {
      return this.destination.icon;
    }

    // Check for the Save as PDF id first.
    if (this.selectedValue === PDF_DESTINATION_KEY) {
      return 'cr:insert-drive-file';
    }

    // Otherwise, must be in the recent list.
    const recent = this.recentDestinationList.find(d => {
      return d.key === this.selectedValue;
    });
    if (recent && recent.icon) {
      return recent.icon;
    }

    // The key/recent destinations don't have information about what icon to
    // use, so just return the generic print icon for now. It will be updated
    // when the destination is set.
    return 'print-preview:print';
  }

  /**
   * @return An inline svg corresponding to the icon for the current
   *     destination and the image for the dropdown arrow.
   */
  private getBackgroundImages_(): string {
    const icon = this.getDestinationIcon_();
    if (!icon) {
      return '';
    }

    let iconSetAndIcon = null;
    if (this.noDestinations) {
      iconSetAndIcon = ['cr', 'error'];
    }
    iconSetAndIcon = iconSetAndIcon || icon.split(':');
    const iconset = this.meta_.byKey(iconSetAndIcon[0]);
    return getSelectDropdownBackground(iconset, iconSetAndIcon[1], this);
  }

  override onProcessSelectChange(value: string) {
    this.dispatchEvent(new CustomEvent(
        'selected-option-change',
        {bubbles: true, composed: true, detail: value}));
  }

  /**
   * Return the options currently visible to the user for testing purposes.
   */
  getVisibleItemsForTest(): NodeListOf<HTMLOptionElement> {
    return this.shadowRoot!.querySelectorAll<HTMLOptionElement>(
        'option:not([hidden])');
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'print-preview-destination-select': PrintPreviewDestinationSelectElement;
  }
}

customElements.define(
    PrintPreviewDestinationSelectElement.is,
    PrintPreviewDestinationSelectElement);