chromium/ash/webui/common/resources/cr_elements/cr_expand_button/cr_expand_button.ts

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

/**
 * @fileoverview
 * 'cr-expand-button' is a chrome-specific wrapper around a button that toggles
 * between an opened (expanded) and closed state.
 *
 * Forked from
 * ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.ts
 */
import '../cr_actionable_row_style.css.js';
import '../cr_icon_button/cr_icon_button.js';
import '../cr_shared_vars.css.js';
import '../icons.html.js';

import {focusWithoutInk} from '//resources/js/focus_without_ink.js';
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {CrIconButtonElement} from '../cr_icon_button/cr_icon_button.js';

import {getTemplate} from './cr_expand_button.html.js';

export interface CrExpandButtonElement {
  $: {
    icon: CrIconButtonElement,
  };
}

export class CrExpandButtonElement extends PolymerElement {
  static get is() {
    return 'cr-expand-button';
  }

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

  static get properties() {
    return {
      /**
       * If true, the button is in the expanded state and will show the icon
       * specified in the `collapseIcon` property. If false, the button shows
       * the icon specified in the `expandIcon` property.
       */
      expanded: {
        type: Boolean,
        value: false,
        notify: true,
        observer: 'onExpandedChange_',
      },

      /**
       * If true, the button will be disabled and grayed out.
       */
      disabled: {
        type: Boolean,
        value: false,
        reflectToAttribute: true,
      },

      /** A11y text descriptor for this control. */
      ariaLabel: {
        type: String,
        observer: 'onAriaLabelChange_',
      },

      tabIndex: {
        type: Number,
        value: 0,
      },

      expandIcon: {
        type: String,
        value: 'cr:expand-more',
        observer: 'onIconChange_',
      },

      collapseIcon: {
        type: String,
        value: 'cr:expand-less',
        observer: 'onIconChange_',
      },

      expandTitle: String,
      collapseTitle: String,

      tooltipText_: {
        type: String,
        computed: 'computeTooltipText_(expandTitle, collapseTitle, expanded)',
        observer: 'onTooltipTextChange_',
      },
    };
  }

  expanded: boolean;
  disabled: boolean;
  expandIcon: string;
  collapseIcon: string;
  expandTitle: string;
  collapseTitle: string;
  private tooltipText_: string;

  static get observers() {
    return ['updateAriaExpanded_(disabled, expanded)'];
  }

  override ready() {
    super.ready();
    this.addEventListener('click', this.toggleExpand_);
  }

  private computeTooltipText_(): string {
    return this.expanded ? this.collapseTitle : this.expandTitle;
  }

  private onTooltipTextChange_() {
    this.title = this.tooltipText_;
  }

  override focus() {
    this.$.icon.focus();
  }

  private onAriaLabelChange_() {
    if (this.ariaLabel) {
      this.$.icon.removeAttribute('aria-labelledby');
      this.$.icon.setAttribute('aria-label', this.ariaLabel);
    } else {
      this.$.icon.removeAttribute('aria-label');
      this.$.icon.setAttribute('aria-labelledby', 'label');
    }
  }

  private onExpandedChange_() {
    this.updateIcon_();
  }

  private onIconChange_() {
    this.updateIcon_();
  }

  private updateIcon_() {
    this.$.icon.ironIcon = this.expanded ? this.collapseIcon : this.expandIcon;
  }

  private toggleExpand_(event: Event) {
    // Prevent |click| event from bubbling. It can cause parents of this
    // elements to erroneously re-toggle this control.
    event.stopPropagation();
    event.preventDefault();

    this.scrollIntoViewIfNeeded();
    this.expanded = !this.expanded;
    focusWithoutInk(this.$.icon);
  }

  private updateAriaExpanded_() {
    if (this.disabled) {
      this.$.icon.removeAttribute('aria-expanded');
    } else {
      this.$.icon.setAttribute(
          'aria-expanded', this.expanded ? 'true' : 'false');
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'cr-expand-button': CrExpandButtonElement;
  }
}

customElements.define(CrExpandButtonElement.is, CrExpandButtonElement);