1. 1 : /**
  2. 2 : * @file menu-item.js
  3. 3 : */
  4. 4 : import ClickableComponent from '../clickable-component.js';
  5. 5 : import Component from '../component.js';
  6. 6 : import {assign} from '../utils/obj';
  7. 7 : import {MenuKeys} from './menu-keys.js';
  8. 8 : import keycode from 'keycode';
  9. 9 : import {createEl} from '../utils/dom.js';
  10. 10 :
  11. 11 : /**
  12. 12 : * The component for a menu item. `<li>`
  13. 13 : *
  14. 14 : * @extends ClickableComponent
  15. 15 : */
  16. 16 : class MenuItem extends ClickableComponent {
  17. 17 :
  18. 18 : /**
  19. 19 : * Creates an instance of the this class.
  20. 20 : *
  21. 21 : * @param {Player} player
  22. 22 : * The `Player` that this class should be attached to.
  23. 23 : *
  24. 24 : * @param {Object} [options={}]
  25. 25 : * The key/value store of player options.
  26. 26 : *
  27. 27 : */
  28. 28 : constructor(player, options) {
  29. 29 : super(player, options);
  30. 30 :
  31. 31 : this.selectable = options.selectable;
  32. 32 : this.isSelected_ = options.selected || false;
  33. 33 : this.multiSelectable = options.multiSelectable;
  34. 34 :
  35. 35 : this.selected(this.isSelected_);
  36. 36 :
  37. 37 : if (this.selectable) {
  38. 38 : if (this.multiSelectable) {
  39. 39 : this.el_.setAttribute('role', 'menuitemcheckbox');
  40. 40 : } else {
  41. 41 : this.el_.setAttribute('role', 'menuitemradio');
  42. 42 : }
  43. 43 : } else {
  44. 44 : this.el_.setAttribute('role', 'menuitem');
  45. 45 : }
  46. 46 : }
  47. 47 :
  48. 48 : /**
  49. 49 : * Create the `MenuItem's DOM element
  50. 50 : *
  51. 51 : * @param {string} [type=li]
  52. 52 : * Element's node type, not actually used, always set to `li`.
  53. 53 : *
  54. 54 : * @param {Object} [props={}]
  55. 55 : * An object of properties that should be set on the element
  56. 56 : *
  57. 57 : * @param {Object} [attrs={}]
  58. 58 : * An object of attributes that should be set on the element
  59. 59 : *
  60. 60 : * @return {Element}
  61. 61 : * The element that gets created.
  62. 62 : */
  63. 63 : createEl(type, props, attrs) {
  64. 64 : // The control is textual, not just an icon
  65. 65 : this.nonIconControl = true;
  66. 66 :
  67. 67 : const el = super.createEl('li', assign({
  68. 68 : className: 'vjs-menu-item',
  69. 69 : tabIndex: -1
  70. 70 : }, props), attrs);
  71. 71 :
  72. 72 : // swap icon with menu item text.
  73. 73 : el.replaceChild(createEl('span', {
  74. 74 : className: 'vjs-menu-item-text',
  75. 75 : textContent: this.localize(this.options_.label)
  76. 76 : }), el.querySelector('.vjs-icon-placeholder'));
  77. 77 :
  78. 78 : return el;
  79. 79 : }
  80. 80 :
  81. 81 : /**
  82. 82 : * Ignore keys which are used by the menu, but pass any other ones up. See
  83. 83 : * {@link ClickableComponent#handleKeyDown} for instances where this is called.
  84. 84 : *
  85. 85 : * @param {EventTarget~Event} event
  86. 86 : * The `keydown` event that caused this function to be called.
  87. 87 : *
  88. 88 : * @listens keydown
  89. 89 : */
  90. 90 : handleKeyDown(event) {
  91. 91 : if (!MenuKeys.some((key) => keycode.isEventKey(event, key))) {
  92. 92 : // Pass keydown handling up for unused keys
  93. 93 : super.handleKeyDown(event);
  94. 94 : }
  95. 95 : }
  96. 96 :
  97. 97 : /**
  98. 98 : * Any click on a `MenuItem` puts it into the selected state.
  99. 99 : * See {@link ClickableComponent#handleClick} for instances where this is called.
  100. 100 : *
  101. 101 : * @param {EventTarget~Event} event
  102. 102 : * The `keydown`, `tap`, or `click` event that caused this function to be
  103. 103 : * called.
  104. 104 : *
  105. 105 : * @listens tap
  106. 106 : * @listens click
  107. 107 : */
  108. 108 : handleClick(event) {
  109. 109 : this.selected(true);
  110. 110 : }
  111. 111 :
  112. 112 : /**
  113. 113 : * Set the state for this menu item as selected or not.
  114. 114 : *
  115. 115 : * @param {boolean} selected
  116. 116 : * if the menu item is selected or not
  117. 117 : */
  118. 118 : selected(selected) {
  119. 119 : if (this.selectable) {
  120. 120 : if (selected) {
  121. 121 : this.addClass('vjs-selected');
  122. 122 : this.el_.setAttribute('aria-checked', 'true');
  123. 123 : // aria-checked isn't fully supported by browsers/screen readers,
  124. 124 : // so indicate selected state to screen reader in the control text.
  125. 125 : this.controlText(', selected');
  126. 126 : this.isSelected_ = true;
  127. 127 : } else {
  128. 128 : this.removeClass('vjs-selected');
  129. 129 : this.el_.setAttribute('aria-checked', 'false');
  130. 130 : // Indicate un-selected state to screen reader
  131. 131 : this.controlText('');
  132. 132 : this.isSelected_ = false;
  133. 133 : }
  134. 134 : }
  135. 135 : }
  136. 136 : }
  137. 137 :
  138. 138 : Component.registerComponent('MenuItem', MenuItem);
  139. 139 : export default MenuItem;