chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.ts

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

import '../cr_icon_button/cr_icon_button.js';
import '../icons_lit.html.js';
import './cr_toolbar_search_field.js';

import {assert} from '//resources/js/assert.js';
import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';

import {getCss} from './cr_toolbar.css.js';
import {getHtml} from './cr_toolbar.html.js';
import type {CrToolbarSearchFieldElement} from './cr_toolbar_search_field.js';

export interface CrToolbarElement {
  $: {
    search: CrToolbarSearchFieldElement,
  };
}

export class CrToolbarElement extends CrLitElement {
  static get is() {
    return 'cr-toolbar';
  }

  static override get styles() {
    return getCss();
  }

  override render() {
    return getHtml.bind(this)();
  }

  static override get properties() {
    return {
      // Name to display in the toolbar, in titlecase.
      pageName: {type: String},

      // Prompt text to display in the search field.
      searchPrompt: {type: String},

      // Tooltip to display on the clear search button.
      clearLabel: {type: String},

      // Tooltip to display on the menu button.
      menuLabel: {type: String},

      // Value is proxied through to cr-toolbar-search-field. When true,
      // the search field will show a processing spinner.
      spinnerActive: {type: Boolean},

      // Controls whether the menu button is shown at the start of the menu.
      showMenu: {type: Boolean},

      // Controls whether the search field is shown.
      showSearch: {type: Boolean},

      // Controls whether the search field is autofocused.
      autofocus: {
        type: Boolean,
        reflect: true,
      },

      // True when the toolbar is displaying in narrow mode.
      narrow: {
        type: Boolean,
        reflect: true,
        notify: true,
      },

      /**
       * The threshold at which the toolbar will change from normal to narrow
       * mode, in px.
       */
      narrowThreshold: {
        type: Number,
      },

      alwaysShowLogo: {
        type: Boolean,
        reflect: true,
      },

      showingSearch_: {
        type: Boolean,
        reflect: true,
      },

      searchIconOverride: {type: String},
      searchInputAriaDescription: {type: String},
    };
  }

  pageName: string = '';
  searchPrompt: string = '';
  clearLabel: string = '';
  menuLabel?: string;
  spinnerActive: boolean = false;
  showMenu: boolean = false;
  showSearch: boolean = true;
  override autofocus: boolean = false;
  narrow: boolean = false;
  narrowThreshold: number = 900;
  alwaysShowLogo: boolean = false;
  protected showingSearch_: boolean = false;
  searchIconOverride?: string;
  searchInputAriaDescription: string = '';
  private narrowQuery_: MediaQueryList|null = null;

  override willUpdate(changedProperties: PropertyValues<this>) {
    super.willUpdate(changedProperties);
    if (changedProperties.has('narrowThreshold')) {
      this.narrowQuery_ =
          window.matchMedia(`(max-width: ${this.narrowThreshold}px)`);
      this.narrow = this.narrowQuery_.matches;
      this.narrowQuery_.addListener(() => this.onQueryChanged_());
    }
  }

  getSearchField(): CrToolbarSearchFieldElement {
    return this.$.search;
  }

  protected onMenuClick_() {
    this.fire('cr-toolbar-menu-click');
  }

  async focusMenuButton() {
    assert(this.showMenu);
    // Wait for rendering to finish to ensure menuButton exists on the DOM.
    await this.updateComplete;
    const menuButton =
        this.shadowRoot!.querySelector<HTMLElement>('#menuButton');
    assert(!!menuButton);
    menuButton.focus();
  }

  isMenuFocused(): boolean {
    return !!this.shadowRoot!.activeElement &&
        this.shadowRoot!.activeElement.id === 'menuButton';
  }

  protected onShowingSearchChanged_(e: CustomEvent<{value: boolean}>) {
    this.showingSearch_ = e.detail.value;
  }

  private onQueryChanged_() {
    assert(this.narrowQuery_);
    this.narrow = this.narrowQuery_.matches;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'cr-toolbar': CrToolbarElement;
  }
}

customElements.define(CrToolbarElement.is, CrToolbarElement);