chromium/ash/webui/print_preview_cros/resources/js/destination_dropdown.ts

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

import '../css/print_preview_cros_shared.css.js';
import './destination_row.js';

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

import {getTemplate} from './destination_dropdown.html.js';
import {DESTINATION_DROPDOWN_DROPDOWN_DISABLED_CHANGED, DESTINATION_DROPDOWN_UPDATE_DESTINATIONS, DESTINATION_DROPDOWN_UPDATE_SELECTED_DESTINATION, DestinationDropdownController} from './destination_dropdown_controller.js';
import {type DestinationRowElement} from './destination_row.js';
import {Destination} from './utils/print_preview_cros_app_types.js';


/**
 * @fileoverview
 * 'destination-dropdown' displays the print job selected destination when the
 * dropdown is closed. When the dropdown is open it displays a list of digital
 * and recent destinations as well as an option to open the destination dialog.
 */

export class DestinationDropdownElement extends PolymerElement {
  static get is() {
    return 'destination-dropdown' as const;
  }

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

  static get properties() {
    return {
      destinations: Array,
      disabled: {
        type: Boolean,
        reflectToAttribute: true,
      },
      open: Boolean,
      selectedDestination: Object,
    };
  }

  disabled: boolean;
  private controller: DestinationDropdownController;
  private eventTracker = new EventTracker();
  private destinations: Destination[] = [];
  private open = false;
  private selectedDestination: Destination|null;

  override connectedCallback(): void {
    super.connectedCallback();
    this.controller = new DestinationDropdownController(this.eventTracker);


    this.eventTracker.add(
        this.controller, DESTINATION_DROPDOWN_DROPDOWN_DISABLED_CHANGED,
        (): void => this.onDestinationDropdownDisabledChanged());
    this.eventTracker.add(
        this.controller, DESTINATION_DROPDOWN_UPDATE_SELECTED_DESTINATION,
        (): void => this.onDestinationDropdownUpdateSelectedDestination());
    this.eventTracker.add(
        this.controller, DESTINATION_DROPDOWN_UPDATE_DESTINATIONS,
        (): void => this.onDestinationDropdownUpdateDestinations());

    // Initialize properties using the controller.
    this.selectedDestination = this.controller.getSelectedDestination();
    this.destinations = this.controller.getDestinations();
    this.disabled = this.controller.shouldDisableDropdown();
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback();
    this.eventTracker.removeAll();
  }

  // Handles toggling visibility of dropdown content.
  onSelectedClicked(): void {
    if (this.disabled) {
      return;
    }

    this.open = !this.open;
  }

  // Handles passing the ID of the destination row that was clicked to the
  // controller for processing and closes the dropdown menu.
  onDestinationClicked(event: Event): void {
    assert(event.target);
    const row: DestinationRowElement = event.target as DestinationRowElement;
    assert(row.destination);

    // Clicked on currently selected destination.
    if (this.selectedDestination?.id === row.destination.id) {
      this.open = false;
      return;
    }

    const destinationUpdated =
        this.controller.updateActiveDestination(row.destination!.id);
    if (destinationUpdated) {
      // Immediately update UI to display selected destination.
      this.selectedDestination = row.destination;
    }

    this.open = false;
  }

  // Handles updating disabled state.
  private onDestinationDropdownDisabledChanged(): void {
    this.disabled = this.controller.shouldDisableDropdown();

    // Ensure content is closed if control disabled to avoid content being
    // locked open.
    if (this.disabled) {
      this.open = false;
    }
  }

  // Handles updating dropdown UI content when update content event occurs.
  private onDestinationDropdownUpdateDestinations(): void {
    // Use spread operator to ensure Polymer registers array update.
    this.destinations = [...this.controller.getDestinations()];
  }

  // Handles updating UI when update selected destination event occurs.
  private onDestinationDropdownUpdateSelectedDestination(): void {
    this.selectedDestination = this.controller.getSelectedDestination();
  }

  getControllerForTesting(): DestinationDropdownController {
    return this.controller;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [DestinationDropdownElement.is]: DestinationDropdownElement;
  }
}

customElements.define(
    DestinationDropdownElement.is, DestinationDropdownElement);