ExamplesPlaygroundReference Source

coral-spectrum/coral-component-cyclebutton/src/scripts/CycleButtonItem.js

  1. /**
  2. * Copyright 2019 Adobe. All rights reserved.
  3. * This file is licensed to you under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License. You may obtain a copy
  5. * of the License at http://www.apache.org/licenses/LICENSE-2.0
  6. *
  7. * Unless required by applicable law or agreed to in writing, software distributed under
  8. * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
  9. * OF ANY KIND, either express or implied. See the License for the specific language
  10. * governing permissions and limitations under the License.
  11. */
  12.  
  13. import {BaseComponent} from '../../../coral-base-component';
  14. import {commons, transform, validate} from '../../../coral-utils';
  15. import {Decorator} from '../../../coral-decorator';
  16.  
  17. /**
  18. Enum for {CycleButtonItem} display options.
  19.  
  20. @typedef {Object} CycleButtonItemDisplayModeEnum
  21.  
  22. @property {String} ICON
  23. Icon display mode.
  24. @property {String} TEXT
  25. Text display mode.
  26. @property {String} ICON_TEXT
  27. Icon and text display mode.
  28. @property {String} INHERIT
  29. Inherit display mode.
  30. */
  31. const displayMode = {
  32. ICON: 'icon',
  33. TEXT: 'text',
  34. ICON_TEXT: 'icontext',
  35. INHERIT: 'inherit'
  36. };
  37.  
  38. /**
  39. @class Coral.CycleButton.Item
  40. @classdesc A CycleButton Item component
  41. @htmltag coral-cyclebutton-item
  42. @extends {HTMLElement}
  43. @extends {BaseComponent}
  44. */
  45. const CycleButtonItem = Decorator(class extends BaseComponent(HTMLElement) {
  46. /**
  47. The Item's icon. See {@link Coral.Icon} for valid icon names.
  48.  
  49. @type {String}
  50. @default ""
  51. @htmlattribute icon
  52. @htmlattributereflected
  53. */
  54. get icon() {
  55. return this._icon || '';
  56. }
  57.  
  58. set icon(value) {
  59. let _icon = transform.string(value);
  60.  
  61. if(this._icon === _icon) {
  62. return;
  63. }
  64.  
  65. this._icon = _icon;
  66. this._reflectAttribute('icon', this._icon);
  67.  
  68. this.trigger('coral-cyclebutton-item:_iconchanged');
  69. }
  70.  
  71. // @compat
  72. get content() {
  73. return this;
  74. }
  75.  
  76. set content(value) {
  77. // Support configs
  78. if (typeof value === 'object') {
  79. for (const prop in value) {
  80. /** @ignore */
  81. this[prop] = value[prop];
  82. }
  83. }
  84. }
  85.  
  86. /**
  87. Whether the Item is disabled. When set to true, this will prevent every user interacting with it.
  88.  
  89. @type {Boolean}
  90. @default false
  91. @htmlattribute disabled
  92. @htmlattributereflected
  93. */
  94. get disabled() {
  95. return this._disabled || false;
  96. }
  97.  
  98. set disabled(value) {
  99. this._disabled = transform.booleanAttr(value);
  100. this._reflectAttribute('disabled', this._disabled);
  101.  
  102. this.classList.toggle('is-disabled', this.disabled);
  103. this[this._disabled ? 'setAttribute' : 'removeAttribute']('aria-disabled', this._disabled);
  104.  
  105. if (this._disabled && this.selected) {
  106. this.selected = false;
  107. }
  108.  
  109. if (!this._disabled && !this.selected) {
  110. // We inform the parent to verify if this item should be selected because it's the only one left
  111. this.trigger('coral-cyclebutton-item:_validateselection');
  112. }
  113. }
  114.  
  115. /**
  116. Whether the Item is selected.
  117. @type {Boolean}
  118. @default false
  119. @htmlattribute selected
  120. @htmlattributereflected
  121. */
  122. get selected() {
  123. return this._selected || false;
  124. }
  125.  
  126. set selected(value) {
  127. let _selected = transform.booleanAttr(value);
  128.  
  129. if(this._selected === _selected) {
  130. return;
  131. }
  132. if (!_selected || _selected && !this.disabled) {
  133. this._selected = _selected;
  134. this._reflectAttribute('selected', this.disabled ? false : this._selected);
  135.  
  136. this.classList.toggle('is-selected', this._selected);
  137. this.setAttribute('aria-checked', this._selected);
  138.  
  139. this.trigger('coral-cyclebutton-item:_selectedchanged');
  140. }
  141. }
  142.  
  143. /**
  144. The displayMode to be used when the particular item is selected. When this value is set to <code>inherit</code>
  145. it will defer to the component level displayMode. If the selected item does not have the necessary icon or text
  146. information, then fallback to show whichever is available. The appearance of collapsed items in the popover are
  147. not affected by this property.
  148. See {@link CycleButtonItemDisplayModeEnum}.
  149.  
  150. @type {String}
  151. @default CycleButtonItemDisplayModeEnum.INHERIT
  152. @htmlattribute displaymode
  153. @htmlattributereflected
  154. */
  155. get displayMode() {
  156. return this._displayMode || displayMode.INHERIT;
  157. }
  158.  
  159. set displayMode(value) {
  160. let _value = transform.string(value).toLowerCase();
  161. let _displayMode = validate.enumeration(displayMode)(_value) && _value || displayMode.INHERIT;
  162.  
  163. if(this._displayMode === _displayMode) {
  164. return;
  165. }
  166.  
  167. this._displayMode = _displayMode;
  168. this._reflectAttribute('displaymode', this._displayMode);
  169.  
  170. this.trigger('coral-cyclebutton-item:_displaymodechanged');
  171. }
  172.  
  173. /**
  174. Inherited from {@link BaseComponent#trackingElement}.
  175. */
  176. get trackingElement() {
  177. return typeof this._trackingElement === 'undefined' ?
  178. // keep spaces to only 1 max and trim. this mimics native html behaviors
  179. (this.content || this).textContent.replace(/\s{2,}/g, ' ').trim() || this.icon :
  180. this._trackingElement;
  181. }
  182.  
  183. set trackingElement(value) {
  184. super.trackingElement = value;
  185. }
  186.  
  187. /**
  188. Returns {@link CycleButtonItem} display options.
  189.  
  190. @return {CycleButtonItemDisplayModeEnum}
  191. */
  192. static get displayMode() {
  193. return displayMode;
  194. }
  195.  
  196. static get _attributePropertyMap() {
  197. return commons.extend(super._attributePropertyMap, {
  198. displaymode: 'displayMode'
  199. });
  200. }
  201.  
  202. /** @ignore */
  203. static get observedAttributes() {
  204. return super.observedAttributes.concat(['selected', 'disabled', 'icon', 'displaymode']);
  205. }
  206.  
  207. /** @ignore */
  208. render() {
  209. super.render();
  210.  
  211. // adds the role to support accessibility
  212. this.setAttribute('role', 'menuitemradio');
  213.  
  214. // Default reflected attributes
  215. if (!this._displayMode) {
  216. this.displayMode = displayMode.INHERIT;
  217. }
  218. }
  219. });
  220.  
  221. export default CycleButtonItem;