chromium/third_party/google-closure-library/closure/goog/ui/buttonrenderer.js

/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Default renderer for {@link goog.ui.Button}s.
 */

goog.provide('goog.ui.ButtonRenderer');

goog.forwardDeclare('goog.ui.Button');
goog.require('goog.a11y.aria');
goog.require('goog.a11y.aria.Role');
goog.require('goog.a11y.aria.State');
goog.require('goog.asserts');
goog.require('goog.ui.ButtonSide');
goog.require('goog.ui.Component');
goog.require('goog.ui.ControlRenderer');  // circular



/**
 * Default renderer for {@link goog.ui.Button}s.  Extends the superclass with
 * the following button-specific API methods:
 * <ul>
 *   <li>`getValue` - returns the button element's value
 *   <li>`setValue` - updates the button element to reflect its new value
 *   <li>`getTooltip` - returns the button element's tooltip text
 *   <li>`setTooltip` - updates the button element's tooltip text
 *   <li>`setCollapsed` - removes one or both of the button element's
 *       borders
 * </ul>
 * For alternate renderers, see {@link goog.ui.NativeButtonRenderer},
 * {@link goog.ui.CustomButtonRenderer}, and {@link goog.ui.FlatButtonRenderer}.
 * @constructor
 * @extends {goog.ui.ControlRenderer}
 */
goog.ui.ButtonRenderer = function() {
  'use strict';
  goog.ui.ControlRenderer.call(this);
};
goog.inherits(goog.ui.ButtonRenderer, goog.ui.ControlRenderer);
goog.addSingletonGetter(goog.ui.ButtonRenderer);


/**
 * Default CSS class to be applied to the root element of components rendered
 * by this renderer.
 * @type {string}
 */
goog.ui.ButtonRenderer.CSS_CLASS = goog.getCssName('goog-button');


/**
 * Returns the ARIA role to be applied to buttons.
 * @return {goog.a11y.aria.Role|undefined} ARIA role.
 * @override
 */
goog.ui.ButtonRenderer.prototype.getAriaRole = function() {
  'use strict';
  return goog.a11y.aria.Role.BUTTON;
};


/**
 * Updates the button's ARIA (accessibility) state if the button is being
 * treated as a checkbox. Also makes sure that attributes which aren't
 * supported by buttons aren't being added.
 * @param {Element} element Element whose ARIA state is to be updated.
 * @param {goog.ui.Component.State} state Component state being enabled or
 *     disabled.
 * @param {boolean} enable Whether the state is being enabled or disabled.
 * @protected
 * @override
 */
goog.ui.ButtonRenderer.prototype.updateAriaState = function(
    element, state, enable) {
  'use strict';
  switch (state) {
    // If button has CHECKED or SELECTED state, assign aria-pressed
    case goog.ui.Component.State.SELECTED:
    case goog.ui.Component.State.CHECKED:
      goog.asserts.assert(element, 'The button DOM element cannot be null.');
      goog.a11y.aria.setState(element, goog.a11y.aria.State.PRESSED, enable);
      break;
    default:
    case goog.ui.Component.State.OPENED:
    case goog.ui.Component.State.DISABLED:
      goog.ui.ButtonRenderer.base(
          this, 'updateAriaState', element, state, enable);
      break;
  }
};


/** @override */
goog.ui.ButtonRenderer.prototype.createDom = function(button) {
  'use strict';
  var element = goog.ui.ButtonRenderer.base(this, 'createDom', button);
  this.setTooltip(element, button.getTooltip());

  var value = button.getValue();
  if (value) {
    this.setValue(element, value);
  }

  // If this is a toggle button, set ARIA state
  if (button.isSupportedState(goog.ui.Component.State.CHECKED)) {
    this.updateAriaState(
        element, goog.ui.Component.State.CHECKED, button.isChecked());
  }

  return element;
};


/** @override */
goog.ui.ButtonRenderer.prototype.decorate = function(button, element) {
  'use strict';
  // The superclass implementation takes care of common attributes; we only
  // need to set the value and the tooltip.
  element =
      goog.ui.ButtonRenderer.superClass_.decorate.call(this, button, element);

  button.setValueInternal(this.getValue(element));
  button.setTooltipInternal(this.getTooltip(element));

  // If this is a toggle button, set ARIA state
  if (button.isSupportedState(goog.ui.Component.State.CHECKED)) {
    this.updateAriaState(
        element, goog.ui.Component.State.CHECKED, button.isChecked());
  }

  return element;
};


/**
 * Takes a button's root element, and returns the value associated with it.
 * No-op in the base class.
 * @param {Element} element The button's root element.
 * @return {string|undefined} The button's value (undefined if none).
 */
goog.ui.ButtonRenderer.prototype.getValue = goog.nullFunction;


/**
 * Takes a button's root element and a value, and updates the element to reflect
 * the new value.  No-op in the base class.
 * @param {Element} element The button's root element.
 * @param {string} value New value.
 */
goog.ui.ButtonRenderer.prototype.setValue = goog.nullFunction;


/**
 * Takes a button's root element, and returns its tooltip text.
 * @param {Element} element The button's root element.
 * @return {string|undefined} The tooltip text.
 */
goog.ui.ButtonRenderer.prototype.getTooltip = function(element) {
  'use strict';
  return element.title;
};


/**
 * Takes a button's root element and a tooltip string, and updates the element
 * with the new tooltip.
 * @param {Element} element The button's root element.
 * @param {string} tooltip New tooltip text.
 * @protected
 */
goog.ui.ButtonRenderer.prototype.setTooltip = function(element, tooltip) {
  'use strict';
  if (element) {
    // Don't set a title attribute if there isn't a tooltip. Blank title
    // attributes can be interpreted incorrectly by screen readers.
    if (tooltip) {
      element.title = tooltip;
    } else {
      element.removeAttribute('title');
    }
  }
};


/**
 * Collapses the border on one or both sides of the button, allowing it to be
 * combined with the adjacent button(s), forming a single UI componenet with
 * multiple targets.
 * @param {goog.ui.Button} button Button to update.
 * @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for
 *     which borders should be collapsed.
 * @protected
 */
goog.ui.ButtonRenderer.prototype.setCollapsed = function(button, sides) {
  'use strict';
  var isRtl = button.isRightToLeft();
  var collapseLeftClassName =
      goog.getCssName(this.getStructuralCssClass(), 'collapse-left');
  var collapseRightClassName =
      goog.getCssName(this.getStructuralCssClass(), 'collapse-right');

  button.enableClassName(
      isRtl ? collapseRightClassName : collapseLeftClassName,
      !!(sides & goog.ui.ButtonSide.START));
  button.enableClassName(
      isRtl ? collapseLeftClassName : collapseRightClassName,
      !!(sides & goog.ui.ButtonSide.END));
};


/** @override */
goog.ui.ButtonRenderer.prototype.getCssClass = function() {
  'use strict';
  return goog.ui.ButtonRenderer.CSS_CLASS;
};