Source: ui/overflow_menu.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.ui.OverflowMenu');
  7. goog.require('goog.asserts');
  8. goog.require('shaka.ads.AdManager');
  9. goog.require('shaka.log');
  10. goog.require('shaka.ui.Controls');
  11. goog.require('shaka.ui.Element');
  12. goog.require('shaka.ui.Enums');
  13. goog.require('shaka.ui.Locales');
  14. goog.require('shaka.ui.Localization');
  15. goog.require('shaka.ui.Utils');
  16. goog.require('shaka.util.Dom');
  17. goog.require('shaka.util.Iterables');
  18. /**
  19. * @extends {shaka.ui.Element}
  20. * @final
  21. * @export
  22. */
  23. shaka.ui.OverflowMenu = class extends shaka.ui.Element {
  24. /**
  25. * @param {!HTMLElement} parent
  26. * @param {!shaka.ui.Controls} controls
  27. */
  28. constructor(parent, controls) {
  29. super(parent, controls);
  30. /** @private {!shaka.extern.UIConfiguration} */
  31. this.config_ = this.controls.getConfig();
  32. /** @private {HTMLElement} */
  33. this.controlsContainer_ = this.controls.getControlsContainer();
  34. /** @private {!Array.<shaka.extern.IUIElement>} */
  35. this.children_ = [];
  36. this.addOverflowMenuButton_();
  37. this.addOverflowMenu_();
  38. this.createChildren_();
  39. this.eventManager.listen(
  40. this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
  41. this.updateAriaLabel_();
  42. });
  43. this.eventManager.listen(
  44. this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
  45. this.updateAriaLabel_();
  46. });
  47. this.eventManager.listen(
  48. this.adManager, shaka.ads.AdManager.AD_STARTED, () => {
  49. if (this.ad && this.ad.isLinear()) {
  50. shaka.ui.Utils.setDisplay(this.overflowMenuButton_, false);
  51. }
  52. });
  53. this.eventManager.listen(
  54. this.adManager, shaka.ads.AdManager.AD_STOPPED, () => {
  55. shaka.ui.Utils.setDisplay(this.overflowMenuButton_, true);
  56. });
  57. this.eventManager.listen(
  58. this.controls, 'submenuopen', () => {
  59. // Hide the main overflow menu if one of the sub menus has
  60. // been opened.
  61. shaka.ui.Utils.setDisplay(this.overflowMenu_, false);
  62. });
  63. this.eventManager.listen(
  64. this.overflowMenu_, 'touchstart', (event) => {
  65. this.controls.setLastTouchEventTime(Date.now());
  66. event.stopPropagation();
  67. });
  68. this.eventManager.listen(this.overflowMenuButton_, 'click', () => {
  69. this.onOverflowMenuButtonClick_();
  70. });
  71. this.updateAriaLabel_();
  72. if (this.ad && this.ad.isLinear()) {
  73. // There was already an ad.
  74. shaka.ui.Utils.setDisplay(this.overflowMenuButton_, false);
  75. }
  76. }
  77. /** @override */
  78. release() {
  79. this.controlsContainer_ = null;
  80. for (const element of this.children_) {
  81. element.release();
  82. }
  83. this.children_ = [];
  84. super.release();
  85. }
  86. /**
  87. * @param {string} name
  88. * @param {!shaka.extern.IUIElement.Factory} factory
  89. * @export
  90. */
  91. static registerElement(name, factory) {
  92. shaka.ui.OverflowMenu.elementNamesToFactories_.set(name, factory);
  93. }
  94. /**
  95. * @private
  96. */
  97. addOverflowMenu_() {
  98. /** @private {!HTMLElement} */
  99. this.overflowMenu_ = shaka.util.Dom.createHTMLElement('div');
  100. this.overflowMenu_.classList.add('shaka-overflow-menu');
  101. this.overflowMenu_.classList.add('shaka-no-propagation');
  102. this.overflowMenu_.classList.add('shaka-show-controls-on-mouse-over');
  103. this.overflowMenu_.classList.add('shaka-hidden');
  104. this.controlsContainer_.appendChild(this.overflowMenu_);
  105. }
  106. /**
  107. * @private
  108. */
  109. addOverflowMenuButton_() {
  110. /** @private {!HTMLButtonElement} */
  111. this.overflowMenuButton_ = shaka.util.Dom.createButton();
  112. this.overflowMenuButton_.classList.add('shaka-overflow-menu-button');
  113. this.overflowMenuButton_.classList.add('shaka-no-propagation');
  114. this.overflowMenuButton_.classList.add('material-icons-round');
  115. this.overflowMenuButton_.classList.add('shaka-tooltip');
  116. this.overflowMenuButton_.textContent =
  117. shaka.ui.Enums.MaterialDesignIcons.OPEN_OVERFLOW;
  118. this.parent.appendChild(this.overflowMenuButton_);
  119. }
  120. /**
  121. * @private
  122. */
  123. createChildren_() {
  124. for (const name of this.config_.overflowMenuButtons) {
  125. if (shaka.ui.OverflowMenu.elementNamesToFactories_.get(name)) {
  126. const factory =
  127. shaka.ui.OverflowMenu.elementNamesToFactories_.get(name);
  128. goog.asserts.assert(this.controls, 'Controls should not be null!');
  129. this.children_.push(factory.create(this.overflowMenu_, this.controls));
  130. } else {
  131. shaka.log.alwaysWarn('Unrecognized overflow menu element requested:',
  132. name);
  133. }
  134. }
  135. }
  136. /** @private */
  137. onOverflowMenuButtonClick_() {
  138. if (this.controls.anySettingsMenusAreOpen()) {
  139. this.controls.hideSettingsMenus();
  140. } else {
  141. shaka.ui.Utils.setDisplay(this.overflowMenu_, true);
  142. this.controls.computeOpacity();
  143. // If overflow menu has currently visible buttons, focus on the
  144. // first one, when the menu opens.
  145. const isDisplayed =
  146. (element) => element.classList.contains('shaka-hidden') == false;
  147. const Iterables = shaka.util.Iterables;
  148. if (Iterables.some(this.overflowMenu_.childNodes, isDisplayed)) {
  149. // Focus on the first visible child of the overflow menu
  150. const visibleElements =
  151. Iterables.filter(this.overflowMenu_.childNodes, isDisplayed);
  152. /** @type {!HTMLElement} */ (visibleElements[0]).focus();
  153. }
  154. }
  155. }
  156. /**
  157. * @private
  158. */
  159. updateAriaLabel_() {
  160. const LocIds = shaka.ui.Locales.Ids;
  161. this.overflowMenuButton_.ariaLabel =
  162. this.localization.resolve(LocIds.MORE_SETTINGS);
  163. }
  164. };
  165. /**
  166. * @implements {shaka.extern.IUIElement.Factory}
  167. * @final
  168. */
  169. shaka.ui.OverflowMenu.Factory = class {
  170. /** @override */
  171. create(rootElement, controls) {
  172. return new shaka.ui.OverflowMenu(rootElement, controls);
  173. }
  174. };
  175. shaka.ui.Controls.registerElement(
  176. 'overflow_menu', new shaka.ui.OverflowMenu.Factory());
  177. /** @private {!Map.<string, !shaka.extern.IUIElement.Factory>} */
  178. shaka.ui.OverflowMenu.elementNamesToFactories_ = new Map();