clickable-component.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _component = require('./component');
  4. var _component2 = _interopRequireDefault(_component);
  5. var _dom = require('./utils/dom.js');
  6. var Dom = _interopRequireWildcard(_dom);
  7. var _events = require('./utils/events.js');
  8. var Events = _interopRequireWildcard(_events);
  9. var _fn = require('./utils/fn.js');
  10. var Fn = _interopRequireWildcard(_fn);
  11. var _log = require('./utils/log.js');
  12. var _log2 = _interopRequireDefault(_log);
  13. var _document = require('global/document');
  14. var _document2 = _interopRequireDefault(_document);
  15. var _obj = require('./utils/obj');
  16. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  17. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  18. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  19. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  20. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  21. * @file button.js
  22. */
  23. /**
  24. * Clickable Component which is clickable or keyboard actionable,
  25. * but is not a native HTML button.
  26. *
  27. * @extends Component
  28. */
  29. var ClickableComponent = function (_Component) {
  30. _inherits(ClickableComponent, _Component);
  31. /**
  32. * Creates an instance of this class.
  33. *
  34. * @param {Player} player
  35. * The `Player` that this class should be attached to.
  36. *
  37. * @param {Object} [options]
  38. * The key/value store of player options.
  39. */
  40. function ClickableComponent(player, options) {
  41. _classCallCheck(this, ClickableComponent);
  42. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  43. _this.emitTapEvents();
  44. _this.enable();
  45. return _this;
  46. }
  47. /**
  48. * Create the `Component`s DOM element.
  49. *
  50. * @param {string} [tag=div]
  51. * The element's node type.
  52. *
  53. * @param {Object} [props={}]
  54. * An object of properties that should be set on the element.
  55. *
  56. * @param {Object} [attributes={}]
  57. * An object of attributes that should be set on the element.
  58. *
  59. * @return {Element}
  60. * The element that gets created.
  61. */
  62. ClickableComponent.prototype.createEl = function createEl() {
  63. var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  64. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  65. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  66. props = (0, _obj.assign)({
  67. className: this.buildCSSClass(),
  68. tabIndex: 0
  69. }, props);
  70. if (tag === 'button') {
  71. _log2['default'].error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
  72. }
  73. // Add ARIA attributes for clickable element which is not a native HTML button
  74. attributes = (0, _obj.assign)({
  75. 'role': 'button',
  76. // let the screen reader user know that the text of the element may change
  77. 'aria-live': 'polite'
  78. }, attributes);
  79. this.tabIndex_ = props.tabIndex;
  80. var el = _Component.prototype.createEl.call(this, tag, props, attributes);
  81. this.createControlTextEl(el);
  82. return el;
  83. };
  84. /**
  85. * Create a control text element on this `Component`
  86. *
  87. * @param {Element} [el]
  88. * Parent element for the control text.
  89. *
  90. * @return {Element}
  91. * The control text element that gets created.
  92. */
  93. ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
  94. this.controlTextEl_ = Dom.createEl('span', {
  95. className: 'vjs-control-text'
  96. });
  97. if (el) {
  98. el.appendChild(this.controlTextEl_);
  99. }
  100. this.controlText(this.controlText_, el);
  101. return this.controlTextEl_;
  102. };
  103. /**
  104. * Get or set the localize text to use for the controls on the `Component`.
  105. *
  106. * @param {string} [text]
  107. * Control text for element.
  108. *
  109. * @param {Element} [el=this.el()]
  110. * Element to set the title on.
  111. *
  112. * @return {string|ClickableComponent}
  113. * - The control text when getting
  114. * - Returns itself when setting; method can be chained.
  115. */
  116. ClickableComponent.prototype.controlText = function controlText(text) {
  117. var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
  118. if (!text) {
  119. return this.controlText_ || 'Need Text';
  120. }
  121. var localizedText = this.localize(text);
  122. this.controlText_ = text;
  123. this.controlTextEl_.innerHTML = localizedText;
  124. if (!this.nonIconControl) {
  125. // Set title attribute if only an icon is shown
  126. el.setAttribute('title', localizedText);
  127. }
  128. return this;
  129. };
  130. /**
  131. * Builds the default DOM `className`.
  132. *
  133. * @return {string}
  134. * The DOM `className` for this object.
  135. */
  136. ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
  137. return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
  138. };
  139. /**
  140. * Enable this `Component`s element.
  141. *
  142. * @return {ClickableComponent}
  143. * Returns itself; method can be chained.
  144. */
  145. ClickableComponent.prototype.enable = function enable() {
  146. this.removeClass('vjs-disabled');
  147. this.el_.setAttribute('aria-disabled', 'false');
  148. if (typeof this.tabIndex_ !== 'undefined') {
  149. this.el_.setAttribute('tabIndex', this.tabIndex_);
  150. }
  151. this.on('tap', this.handleClick);
  152. this.on('click', this.handleClick);
  153. this.on('focus', this.handleFocus);
  154. this.on('blur', this.handleBlur);
  155. return this;
  156. };
  157. /**
  158. * Disable this `Component`s element.
  159. *
  160. * @return {ClickableComponent}
  161. * Returns itself; method can be chained.
  162. */
  163. ClickableComponent.prototype.disable = function disable() {
  164. this.addClass('vjs-disabled');
  165. this.el_.setAttribute('aria-disabled', 'true');
  166. if (typeof this.tabIndex_ !== 'undefined') {
  167. this.el_.removeAttribute('tabIndex');
  168. }
  169. this.off('tap', this.handleClick);
  170. this.off('click', this.handleClick);
  171. this.off('focus', this.handleFocus);
  172. this.off('blur', this.handleBlur);
  173. return this;
  174. };
  175. /**
  176. * This gets called when a `ClickableComponent` gets:
  177. * - Clicked (via the `click` event, listening starts in the constructor)
  178. * - Tapped (via the `tap` event, listening starts in the constructor)
  179. * - The following things happen in order:
  180. * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
  181. * `ClickableComponent`.
  182. * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
  183. * {@link ClickableComponent#handleKeyPress}.
  184. * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
  185. * the space or enter key.
  186. * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
  187. * event as a parameter.
  188. *
  189. * @param {EventTarget~Event} event
  190. * The `keydown`, `tap`, or `click` event that caused this function to be
  191. * called.
  192. *
  193. * @listens tap
  194. * @listens click
  195. * @abstract
  196. */
  197. ClickableComponent.prototype.handleClick = function handleClick(event) {};
  198. /**
  199. * This gets called when a `ClickableComponent` gains focus via a `focus` event.
  200. * Turns on listening for `keydown` events. When they happen it
  201. * calls `this.handleKeyPress`.
  202. *
  203. * @param {EventTarget~Event} event
  204. * The `focus` event that caused this function to be called.
  205. *
  206. * @listens focus
  207. */
  208. ClickableComponent.prototype.handleFocus = function handleFocus(event) {
  209. Events.on(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
  210. };
  211. /**
  212. * Called when this ClickableComponent has focus and a key gets pressed down. By
  213. * default it will call `this.handleClick` when the key is space or enter.
  214. *
  215. * @param {EventTarget~Event} event
  216. * The `keydown` event that caused this function to be called.
  217. *
  218. * @listens keydown
  219. */
  220. ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
  221. // Support Space (32) or Enter (13) key operation to fire a click event
  222. if (event.which === 32 || event.which === 13) {
  223. event.preventDefault();
  224. this.handleClick(event);
  225. } else if (_Component.prototype.handleKeyPress) {
  226. // Pass keypress handling up for unsupported keys
  227. _Component.prototype.handleKeyPress.call(this, event);
  228. }
  229. };
  230. /**
  231. * Called when a `ClickableComponent` loses focus. Turns off the listener for
  232. * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
  233. *
  234. * @param {EventTarget~Event} event
  235. * The `blur` event that caused this function to be called.
  236. *
  237. * @listens blur
  238. */
  239. ClickableComponent.prototype.handleBlur = function handleBlur(event) {
  240. Events.off(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
  241. };
  242. return ClickableComponent;
  243. }(_component2['default']);
  244. _component2['default'].registerComponent('ClickableComponent', ClickableComponent);
  245. exports['default'] = ClickableComponent;