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

// Copyright 2017 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/md_select.css.js';
import 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import './number_settings_section.js';
import './print_preview_shared.css.js';
import './settings_section.js';

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

import type {Settings} from '../data/model.js';
import {ScalingType} from '../data/scaling.js';

import {getTemplate} from './scaling_settings.html.js';
import {SelectMixin} from './select_mixin.js';
import {SettingsMixin} from './settings_mixin.js';

/*
 * Fit to page and fit to paper options will only be displayed for PDF
 * documents. If the custom option is selected, an additional input field will
 * appear to enter the custom scale factor.
 */

const PrintPreviewScalingSettingsElementBase =
    SettingsMixin(SelectMixin(PolymerElement));

export class PrintPreviewScalingSettingsElement extends
    PrintPreviewScalingSettingsElementBase {
  static get is() {
    return 'print-preview-scaling-settings';
  }

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

  static get properties() {
    return {
      disabled: {
        type: Boolean,
        observer: 'onDisabledChanged_',
      },

      isPdf: Boolean,

      currentValue_: {
        type: String,
      },

      customSelected_: {
        type: Boolean,
        computed: 'computeCustomSelected_(settingKey_, ' +
            'settings.scalingType.*, settings.scalingTypePdf.*)',
      },

      inputValid_: Boolean,

      dropdownDisabled_: {
        type: Boolean,
        value: false,
      },

      settingKey_: {
        type: String,
        computed: 'computeSettingKey_(isPdf)',
      },

      /** Mirroring the enum so that it can be used from HTML bindings. */
      ScalingValue: {
        type: Object,
        value: ScalingType,
      },
    };
  }

  static get observers() {
    return [
      'onScalingTypeSettingChanged_(settingKey_, settings.scalingType.value, ' +
          'settings.scalingTypePdf.value)',
      'onScalingSettingChanged_(settings.scaling.value)',
      'onInputFieldChanged_(inputValid_, currentValue_)',
    ];
  }

  disabled: boolean;
  isPdf: boolean;
  private currentValue_: string;
  private customSelected_: boolean;
  private dropdownDisabled_: boolean;
  private inputValid_: boolean;
  private settingKey_: keyof Settings;

  private lastValidScaling_: string = '';

  /**
   * Whether the custom scaling setting has been set to true, but the custom
   * input has not yet been expanded. Used to determine whether changes in the
   * dropdown are due to user input or sticky settings.
   */
  private customScalingSettingSet_: boolean = false;

  /**
   * Whether the user has selected custom scaling in the dropdown, but the
   * custom input has not yet been expanded. Used to determine whether to
   * auto-focus the custom input.
   */
  private userSelectedCustomScaling_: boolean = false;

  override onProcessSelectChange(value: string) {
    const isCustom = value === ScalingType.CUSTOM.toString();
    if (isCustom && !this.customScalingSettingSet_) {
      this.userSelectedCustomScaling_ = true;
    } else {
      this.customScalingSettingSet_ = false;
    }

    const valueAsNumber = parseInt(value, 10);
    if (isCustom || value === ScalingType.DEFAULT.toString()) {
      this.setSetting('scalingType', valueAsNumber);
    }
    if (this.isPdf ||
        this.getSetting('scalingTypePdf').value === ScalingType.DEFAULT ||
        this.getSetting('scalingTypePdf').value === ScalingType.CUSTOM) {
      this.setSetting('scalingTypePdf', valueAsNumber);
    }

    if (isCustom) {
      this.setSetting('scaling', this.currentValue_);
    }
  }

  private updateScalingToValid_() {
    if (!this.getSetting('scaling').valid) {
      this.currentValue_ = this.lastValidScaling_;
    } else {
      this.lastValidScaling_ = this.currentValue_;
    }
  }

  /**
   * Updates the input string when scaling setting is set.
   */
  private onScalingSettingChanged_() {
    const value = this.getSetting('scaling').value as string;
    this.lastValidScaling_ = value;
    this.currentValue_ = value;
  }

  private onScalingTypeSettingChanged_() {
    if (!this.settingKey_) {
      return;
    }

    const value = this.getSettingValue(this.settingKey_) as ScalingType;
    if (value !== ScalingType.CUSTOM) {
      this.updateScalingToValid_();
    } else {
      this.customScalingSettingSet_ = true;
    }
    this.selectedValue = value.toString();
  }

  /**
   * Updates scaling settings based on the validity and current value of the
   * scaling input.
   */
  private onInputFieldChanged_() {
    this.setSettingValid('scaling', this.inputValid_);

    if (this.currentValue_ !== undefined && this.currentValue_ !== '' &&
        this.inputValid_ &&
        this.currentValue_ !== this.getSettingValue('scaling')) {
      this.setSetting('scaling', this.currentValue_);
    }
  }

  private onDisabledChanged_() {
    this.dropdownDisabled_ = this.disabled && this.inputValid_;
  }

  /**
   * @return Whether the input should be disabled.
   */
  private inputDisabled_(): boolean {
    return !this.customSelected_ || this.dropdownDisabled_;
  }

  /**
   * @return Whether the custom scaling option is selected.
   */
  private computeCustomSelected_(): boolean {
    return !!this.settingKey_ &&
        this.getSettingValue(this.settingKey_) === ScalingType.CUSTOM;
  }

  /**
   * @return The key of the appropriate scaling setting.
   */
  private computeSettingKey_(): string {
    return this.isPdf ? 'scalingTypePdf' : 'scalingType';
  }

  private onCollapseChanged_() {
    if (this.customSelected_ && this.userSelectedCustomScaling_) {
      this.shadowRoot!.querySelector('print-preview-number-settings-section')!
          .getInput()
          .focus();
    }
    this.customScalingSettingSet_ = false;
    this.userSelectedCustomScaling_ = false;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'print-preview-scaling-settings': PrintPreviewScalingSettingsElement;
  }
}

customElements.define(
    PrintPreviewScalingSettingsElement.is, PrintPreviewScalingSettingsElement);