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.Utils');
  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.Utils.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.Utils.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. if (!this.controls.isOpaque()) {
  70. return;
  71. }
  72. this.onOverflowMenuButtonClick_();
  73. });
  74. this.updateAriaLabel_();
  75. if (this.ad && this.ad.isLinear()) {
  76. // There was already an ad.
  77. shaka.ui.Utils.setDisplay(this.overflowMenuButton_, false);
  78. }
  79. }
  80. /** @override */
  81. release() {
  82. this.controlsContainer_ = null;
  83. for (const element of this.children_) {
  84. element.release();
  85. }
  86. this.children_ = [];
  87. super.release();
  88. }
  89. /**
  90. * @param {string} name
  91. * @param {!shaka.extern.IUIElement.Factory} factory
  92. * @export
  93. */
  94. static registerElement(name, factory) {
  95. shaka.ui.OverflowMenu.elementNamesToFactories_.set(name, factory);
  96. }
  97. /**
  98. * @private
  99. */
  100. addOverflowMenu_() {
  101. /** @private {!HTMLElement} */
  102. this.overflowMenu_ = shaka.util.Dom.createHTMLElement('div');
  103. this.overflowMenu_.classList.add('shaka-overflow-menu');
  104. this.overflowMenu_.classList.add('shaka-no-propagation');
  105. this.overflowMenu_.classList.add('shaka-show-controls-on-mouse-over');
  106. this.overflowMenu_.classList.add('shaka-hidden');
  107. this.controlsContainer_.appendChild(this.overflowMenu_);
  108. }
  109. /**
  110. * @private
  111. */
  112. addOverflowMenuButton_() {
  113. /** @private {!HTMLButtonElement} */
  114. this.overflowMenuButton_ = shaka.util.Dom.createButton();
  115. this.overflowMenuButton_.classList.add('shaka-overflow-menu-button');
  116. this.overflowMenuButton_.classList.add('shaka-no-propagation');
  117. this.overflowMenuButton_.classList.add('material-icons-round');
  118. this.overflowMenuButton_.classList.add('shaka-tooltip');
  119. this.overflowMenuButton_.textContent =
  120. shaka.ui.Enums.MaterialDesignIcons.OPEN_OVERFLOW;
  121. this.parent.appendChild(this.overflowMenuButton_);
  122. }
  123. /**
  124. * @private
  125. */
  126. createChildren_() {
  127. for (const name of this.config_.overflowMenuButtons) {
  128. if (shaka.ui.OverflowMenu.elementNamesToFactories_.get(name)) {
  129. const factory =
  130. shaka.ui.OverflowMenu.elementNamesToFactories_.get(name);
  131. goog.asserts.assert(this.controls, 'Controls should not be null!');
  132. this.children_.push(factory.create(this.overflowMenu_, this.controls));
  133. } else {
  134. shaka.log.alwaysWarn('Unrecognized overflow menu element requested:',
  135. name);
  136. }
  137. }
  138. }
  139. /** @private */
  140. onOverflowMenuButtonClick_() {
  141. if (this.controls.anySettingsMenusAreOpen()) {
  142. this.controls.hideSettingsMenus();
  143. } else {
  144. shaka.ui.Utils.setDisplay(this.overflowMenu_, true);
  145. this.controls.computeOpacity();
  146. // If overflow menu has currently visible buttons, focus on the
  147. // first one, when the menu opens.
  148. const isDisplayed =
  149. (element) => element.classList.contains('shaka-hidden') == false;
  150. const Iterables = shaka.util.Iterables;
  151. if (Iterables.some(this.overflowMenu_.childNodes, isDisplayed)) {
  152. // Focus on the first visible child of the overflow menu
  153. const visibleElements =
  154. Iterables.filter(this.overflowMenu_.childNodes, isDisplayed);
  155. /** @type {!HTMLElement} */ (visibleElements[0]).focus();
  156. }
  157. }
  158. }
  159. /**
  160. * @private
  161. */
  162. updateAriaLabel_() {
  163. const LocIds = shaka.ui.Locales.Ids;
  164. this.overflowMenuButton_.ariaLabel =
  165. this.localization.resolve(LocIds.MORE_SETTINGS);
  166. }
  167. };
  168. /**
  169. * @implements {shaka.extern.IUIElement.Factory}
  170. * @final
  171. */
  172. shaka.ui.OverflowMenu.Factory = class {
  173. /** @override */
  174. create(rootElement, controls) {
  175. return new shaka.ui.OverflowMenu(rootElement, controls);
  176. }
  177. };
  178. shaka.ui.Controls.registerElement(
  179. 'overflow_menu', new shaka.ui.OverflowMenu.Factory());
  180. /** @private {!Map<string, !shaka.extern.IUIElement.Factory>} */
  181. shaka.ui.OverflowMenu.elementNamesToFactories_ = new Map();