chromium/ash/webui/camera_app_ui/resources/js/views/settings/photo_aspect_ratio.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 {CameraManager} from '../../device/index.js';
import {
  PhotoAspectRatioOption,
  PhotoAspectRatioOptionGroup,
} from '../../device/type.js';
import * as dom from '../../dom.js';
import * as loadTimeData from '../../models/load_time_data.js';
import {Facing, ViewName} from '../../type.js';
import {instantiateTemplate, setupI18nElements} from '../../util.js';

import {BaseSettings} from './base.js';
import * as util from './util.js';

/**
 * View controller of photo aspect ratio settings.
 */
export class PhotoAspectRatioSettings extends BaseSettings {
  private readonly menu: HTMLElement;

  private focusedDeviceId: string|null = null;

  private menuScrollTop = 0;

  constructor(readonly cameraManager: CameraManager) {
    super(ViewName.PHOTO_ASPECT_RATIO_SETTINGS);

    this.menu = dom.getFrom(this.root, 'div.menu', HTMLDivElement);
    cameraManager.registerCameraUI({
      onCameraUnavailable: () => {
        for (const input of dom.getAllFrom(
                 this.menu, 'input', HTMLInputElement)) {
          input.disabled = true;
        }
      },
      onCameraAvailable: () => {
        for (const input of dom.getAllFrom(
                 this.menu, 'input', HTMLInputElement)) {
          input.disabled = false;
        }
      },
    });

    this.cameraManager.addPhotoAspectRatioOptionListener(
        (groups) => this.onOptionsUpdate(groups));
  }

  private onOptionsUpdate(groups: PhotoAspectRatioOptionGroup[]): void {
    util.clearMenu(this.menu);
    for (const {deviceId, facing, options} of groups) {
      const deviceLabel = util.getLabelFromFacing(facing);
      const labelOption = instantiateTemplate('#resolution-label-template');
      dom.getFrom(labelOption, 'div', HTMLDivElement)
          .setAttribute('i18n-aria', deviceLabel);
      dom.getFrom(labelOption, 'span', HTMLSpanElement).textContent =
          loadTimeData.getI18nMessage(deviceLabel);
      this.menu.appendChild(labelOption);

      // After moving square mode option into aspect ratio settings, generally
      // it is impossible to have less than two options since "square" will
      // always be an option.
      for (const option of options) {
        this.addAspectRatioItem(deviceId, facing, option);
      }
    }
    setupI18nElements(this.menu);
    this.menu.scrollTop = this.menuScrollTop;
  }

  private addAspectRatioItem(
      deviceId: string, facing: Facing, option: PhotoAspectRatioOption): void {
    const deviceLabel = util.getLabelFromFacing(facing);
    const optionElement = instantiateTemplate('#resolution-item-template');
    const span = dom.getFrom(optionElement, 'span', HTMLSpanElement);
    span.textContent = util.toAspectRatioLabel(option.aspectRatioSet);
    const deviceName = loadTimeData.getI18nMessage(deviceLabel);
    span.setAttribute(
        'aria-label',
        `${deviceName} ${util.toAspectRatioAriaLabel(option.aspectRatioSet)}`);

    const input = dom.getFrom(optionElement, 'input', HTMLInputElement);
    input.dataset['aspectRatio'] = option.aspectRatioSet.toString();
    input.dataset['facing'] = facing;
    input.name = `photo-aspect-ratio-${deviceId}`;
    input.checked = option.checked;

    if (!input.checked) {
      input.addEventListener('click', async (event) => {
        event.preventDefault();
        this.focusedDeviceId = deviceId;
        this.menuScrollTop = this.menu.scrollTop;
        await this.cameraManager.setPrefPhotoAspectRatioSet(
            deviceId, option.aspectRatioSet);
      });
    }
    this.menu.appendChild(optionElement);

    if (input.checked && this.focusedDeviceId === deviceId) {
      input.focus();
    }
  }
}