Path: blob/trunk/third_party/closure/goog/ui/menuitemrenderer.js
4122 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Renderer for {@link goog.ui.MenuItem}s.8*/910goog.provide('goog.ui.MenuItemRenderer');1112goog.require('goog.a11y.aria.Role');13goog.require('goog.asserts');14goog.require('goog.dom');15goog.require('goog.dom.TagName');16goog.require('goog.dom.classlist');17goog.require('goog.ui.Component');18goog.require('goog.ui.ControlRenderer');19goog.requireType('goog.ui.Control');20goog.requireType('goog.ui.ControlContent');21goog.requireType('goog.ui.MenuItem');22232425/**26* Default renderer for {@link goog.ui.MenuItem}s. Each item has the following27* structure:28*29* <div class="goog-menuitem">30* <div class="goog-menuitem-content">31* ...(menu item contents)...32* </div>33* </div>34*35* @constructor36* @extends {goog.ui.ControlRenderer}37*/38goog.ui.MenuItemRenderer = function() {39'use strict';40goog.ui.ControlRenderer.call(this);4142/**43* Commonly used CSS class names, cached here for convenience (and to avoid44* unnecessary string concatenation).45* @type {!Array<string>}46* @private47*/48this.classNameCache_ = [];49};50goog.inherits(goog.ui.MenuItemRenderer, goog.ui.ControlRenderer);51goog.addSingletonGetter(goog.ui.MenuItemRenderer);525354/**55* CSS class name the renderer applies to menu item elements.56* @type {string}57*/58goog.ui.MenuItemRenderer.CSS_CLASS = goog.getCssName('goog-menuitem');596061/**62* Constants for referencing composite CSS classes.63* @enum {number}64* @private65*/66goog.ui.MenuItemRenderer.CompositeCssClassIndex_ = {67HOVER: 0,68CHECKBOX: 1,69CONTENT: 270};717273/**74* Returns the composite CSS class by using the cached value or by constructing75* the value from the base CSS class and the passed index.76* @param {goog.ui.MenuItemRenderer.CompositeCssClassIndex_} index Index for the77* CSS class - could be highlight, checkbox or content in usual cases.78* @return {string} The composite CSS class.79* @private80*/81goog.ui.MenuItemRenderer.prototype.getCompositeCssClass_ = function(index) {82'use strict';83var result = this.classNameCache_[index];84if (!result) {85switch (index) {86case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER:87result = goog.getCssName(this.getStructuralCssClass(), 'highlight');88break;89case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX:90result = goog.getCssName(this.getStructuralCssClass(), 'checkbox');91break;92case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT:93result = goog.getCssName(this.getStructuralCssClass(), 'content');94break;95}96this.classNameCache_[index] = result;97}9899return result;100};101102103/** @override */104goog.ui.MenuItemRenderer.prototype.getAriaRole = function() {105'use strict';106return goog.a11y.aria.Role.MENU_ITEM;107};108109110/**111* Overrides {@link goog.ui.ControlRenderer#createDom} by adding extra markup112* and stying to the menu item's element if it is selectable or checkable.113* @param {goog.ui.Control} item Menu item to render.114* @return {!Element} Root element for the item.115* @override116*/117goog.ui.MenuItemRenderer.prototype.createDom = function(item) {118'use strict';119var element = item.getDomHelper().createDom(120goog.dom.TagName.DIV, this.getClassNames(item).join(' '),121this.createContent(item.getContent(), item.getDomHelper()));122this.setEnableCheckBoxStructure(123item, element, item.isSupportedState(goog.ui.Component.State.SELECTED) ||124item.isSupportedState(goog.ui.Component.State.CHECKED));125return element;126};127128129/** @override */130goog.ui.MenuItemRenderer.prototype.getContentElement = function(element) {131'use strict';132return /** @type {Element} */ (element && element.firstChild);133};134135136/**137* Overrides {@link goog.ui.ControlRenderer#decorate} by initializing the138* menu item to checkable based on whether the element to be decorated has139* extra stying indicating that it should be.140* @param {goog.ui.Control} item Menu item instance to decorate the element.141* @param {Element} element Element to decorate.142* @return {Element} Decorated element.143* @override144*/145goog.ui.MenuItemRenderer.prototype.decorate = function(item, element) {146'use strict';147goog.asserts.assert(element);148if (!this.hasContentStructure(element)) {149element.appendChild(150/** @type {!Node} */ (151this.createContent(element.childNodes, item.getDomHelper())));152}153if (goog.dom.classlist.contains(element, goog.getCssName('goog-option'))) {154(/** @type {goog.ui.MenuItem} */ (item)).setCheckable(true);155this.setCheckable(item, element, true);156}157return goog.ui.MenuItemRenderer.superClass_.decorate.call(158this, item, element);159};160161162/**163* Takes a menu item's root element, and sets its content to the given text164* caption or DOM structure. Overrides the superclass immplementation by165* making sure that the checkbox structure (for selectable/checkable menu166* items) is preserved.167* @param {Element} element The item's root element.168* @param {goog.ui.ControlContent} content Text caption or DOM structure to be169* set as the item's content.170* @override171*/172goog.ui.MenuItemRenderer.prototype.setContent = function(element, content) {173'use strict';174// Save the checkbox element, if present.175var contentElement = this.getContentElement(element);176var checkBoxElement =177this.hasCheckBoxStructure(element) ? contentElement.firstChild : null;178goog.ui.MenuItemRenderer.superClass_.setContent.call(this, element, content);179if (checkBoxElement && !this.hasCheckBoxStructure(element)) {180// The call to setContent() blew away the checkbox element; reattach it.181contentElement.insertBefore(182checkBoxElement, contentElement.firstChild || null);183}184};185186187/**188* Returns true if the element appears to have a proper menu item structure by189* checking whether its first child has the appropriate structural class name.190* @param {Element} element Element to check.191* @return {boolean} Whether the element appears to have a proper menu item DOM.192* @protected193*/194goog.ui.MenuItemRenderer.prototype.hasContentStructure = function(element) {195'use strict';196var child = goog.dom.getFirstElementChild(element);197var contentClassName = this.getCompositeCssClass_(198goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT);199return !!child && goog.dom.classlist.contains(child, contentClassName);200};201202203/**204* Wraps the given text caption or existing DOM node(s) in a structural element205* containing the menu item's contents.206* @param {goog.ui.ControlContent} content Menu item contents.207* @param {goog.dom.DomHelper} dom DOM helper for document interaction.208* @return {!Element} Menu item content element.209* @protected210*/211goog.ui.MenuItemRenderer.prototype.createContent = function(content, dom) {212'use strict';213var contentClassName = this.getCompositeCssClass_(214goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT);215return dom.createDom(goog.dom.TagName.DIV, contentClassName, content);216};217218219/**220* Enables/disables radio button semantics on the menu item.221* @param {goog.ui.Control} item Menu item to update.222* @param {Element} element Menu item element to update (may be null if the223* item hasn't been rendered yet).224* @param {boolean} selectable Whether the item should be selectable.225*/226goog.ui.MenuItemRenderer.prototype.setSelectable = function(227item, element, selectable) {228'use strict';229if (item && element) {230this.setEnableCheckBoxStructure(item, element, selectable);231}232};233234235/**236* Enables/disables checkbox semantics on the menu item.237* @param {goog.ui.Control} item Menu item to update.238* @param {Element} element Menu item element to update (may be null if the239* item hasn't been rendered yet).240* @param {boolean} checkable Whether the item should be checkable.241*/242goog.ui.MenuItemRenderer.prototype.setCheckable = function(243item, element, checkable) {244'use strict';245if (item && element) {246this.setEnableCheckBoxStructure(item, element, checkable);247}248};249250251/**252* Determines whether the item contains a checkbox element.253* @param {Element} element Menu item root element.254* @return {boolean} Whether the element contains a checkbox element.255* @protected256*/257goog.ui.MenuItemRenderer.prototype.hasCheckBoxStructure = function(element) {258'use strict';259var contentElement = this.getContentElement(element);260if (contentElement) {261var child = contentElement.firstChild;262var checkboxClassName = this.getCompositeCssClass_(263goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX);264return !!child && goog.dom.isElement(child) &&265goog.dom.classlist.contains(266/** @type {!Element} */ (child), checkboxClassName);267}268return false;269};270271272/**273* Adds or removes extra markup and CSS styling to the menu item to make it274* selectable or non-selectable, depending on the value of the275* `selectable` argument.276* @param {!goog.ui.Control} item Menu item to update.277* @param {!Element} element Menu item element to update.278* @param {boolean} enable Whether to add or remove the checkbox structure.279* @protected280*/281goog.ui.MenuItemRenderer.prototype.setEnableCheckBoxStructure = function(282item, element, enable) {283'use strict';284this.setAriaRole(element, item.getPreferredAriaRole());285this.setAriaStates(item, element);286if (enable != this.hasCheckBoxStructure(element)) {287goog.dom.classlist.enable(element, goog.getCssName('goog-option'), enable);288var contentElement = this.getContentElement(element);289if (enable) {290// Insert checkbox structure.291var checkboxClassName = this.getCompositeCssClass_(292goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX);293contentElement.insertBefore(294item.getDomHelper().createDom(295goog.dom.TagName.DIV, checkboxClassName),296contentElement.firstChild || null);297} else {298// Remove checkbox structure.299contentElement.removeChild(300/** @type {!Node} */ (contentElement.firstChild));301}302}303};304305306/**307* Takes a single {@link goog.ui.Component.State}, and returns the308* corresponding CSS class name (null if none). Overrides the superclass309* implementation by using 'highlight' as opposed to 'hover' as the CSS310* class name suffix for the HOVER state, for backwards compatibility.311* @param {goog.ui.Component.State} state Component state.312* @return {string|undefined} CSS class representing the given state313* (undefined if none).314* @override315*/316goog.ui.MenuItemRenderer.prototype.getClassForState = function(state) {317'use strict';318switch (state) {319case goog.ui.Component.State.HOVER:320// We use 'highlight' as the suffix, for backwards compatibility.321return this.getCompositeCssClass_(322goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER);323case goog.ui.Component.State.CHECKED:324case goog.ui.Component.State.SELECTED:325// We use 'goog-option-selected' as the class, for backwards326// compatibility.327return goog.getCssName('goog-option-selected');328default:329return goog.ui.MenuItemRenderer.superClass_.getClassForState.call(330this, state);331}332};333334335/**336* Takes a single CSS class name which may represent a component state, and337* returns the corresponding component state (0x00 if none). Overrides the338* superclass implementation by treating 'goog-option-selected' as special,339* for backwards compatibility.340* @param {string} className CSS class name, possibly representing a component341* state.342* @return {goog.ui.Component.State} state Component state corresponding343* to the given CSS class (0x00 if none).344* @override345*/346goog.ui.MenuItemRenderer.prototype.getStateFromClass = function(className) {347'use strict';348var hoverClassName = this.getCompositeCssClass_(349goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER);350switch (className) {351case goog.getCssName('goog-option-selected'):352return goog.ui.Component.State.CHECKED;353case hoverClassName:354return goog.ui.Component.State.HOVER;355default:356return goog.ui.MenuItemRenderer.superClass_.getStateFromClass.call(357this, className);358}359};360361362/** @override */363goog.ui.MenuItemRenderer.prototype.getCssClass = function() {364'use strict';365return goog.ui.MenuItemRenderer.CSS_CLASS;366};367368369