1
1
/**
2
2
* @file menu-button.js
3
3
*/
4
- import ClickableComponent from '../clickable-component .js' ;
4
+ import Button from '../button .js' ;
5
5
import Component from '../component.js' ;
6
6
import Menu from './menu.js' ;
7
7
import * as Dom from '../utils/dom.js' ;
8
8
import * as Fn from '../utils/fn.js' ;
9
+ import * as Events from '../utils/events.js' ;
9
10
import toTitleCase from '../utils/to-title-case.js' ;
11
+ import document from 'global/document' ;
10
12
11
13
/**
12
14
* A `MenuButton` class for any popup {@link Menu}.
13
15
*
14
- * @extends ClickableComponent
16
+ * @extends Component
15
17
*/
16
- class MenuButton extends ClickableComponent {
18
+ class MenuButton extends Component {
17
19
18
20
/**
19
21
* Creates an instance of this class.
@@ -27,12 +29,27 @@ class MenuButton extends ClickableComponent {
27
29
constructor ( player , options = { } ) {
28
30
super ( player , options ) ;
29
31
32
+ this . menuButton_ = new Button ( player , options ) ;
33
+
34
+ this . menuButton_ . controlText ( this . controlText_ ) ;
35
+ this . menuButton_ . el_ . setAttribute ( 'aria-haspopup' , 'true' ) ;
36
+
37
+ // Add buildCSSClass values to the button, not the wrapper
38
+ const buttonClass = Button . prototype . buildCSSClass ( ) ;
39
+
40
+ this . menuButton_ . el_ . className = this . buildCSSClass ( ) + ' ' + buttonClass ;
41
+
42
+ this . addChild ( this . menuButton_ ) ;
43
+
30
44
this . update ( ) ;
31
45
32
46
this . enabled_ = true ;
33
47
34
- this . el_ . setAttribute ( 'aria-haspopup' , 'true' ) ;
35
- this . el_ . setAttribute ( 'role' , 'menuitem' ) ;
48
+ this . on ( this . menuButton_ , 'tap' , this . handleClick ) ;
49
+ this . on ( this . menuButton_ , 'click' , this . handleClick ) ;
50
+ this . on ( this . menuButton_ , 'focus' , this . handleFocus ) ;
51
+ this . on ( this . menuButton_ , 'blur' , this . handleBlur ) ;
52
+
36
53
this . on ( 'keydown' , this . handleSubmenuKeyPress ) ;
37
54
}
38
55
@@ -56,7 +73,7 @@ class MenuButton extends ClickableComponent {
56
73
* @private
57
74
*/
58
75
this . buttonPressed_ = false ;
59
- this . el_ . setAttribute ( 'aria-expanded' , 'false' ) ;
76
+ this . menuButton_ . el_ . setAttribute ( 'aria-expanded' , 'false' ) ;
60
77
61
78
if ( this . items && this . items . length === 0 ) {
62
79
this . hide ( ) ;
@@ -72,7 +89,7 @@ class MenuButton extends ClickableComponent {
72
89
* The constructed menu
73
90
*/
74
91
createMenu ( ) {
75
- const menu = new Menu ( this . player_ ) ;
92
+ const menu = new Menu ( this . player_ , { menuButton : this } ) ;
76
93
77
94
// Add a title list item to the top
78
95
if ( this . options_ . title ) {
@@ -113,10 +130,33 @@ class MenuButton extends ClickableComponent {
113
130
*/
114
131
createEl ( ) {
115
132
return super . createEl ( 'div' , {
116
- className : this . buildCSSClass ( )
133
+ className : this . buildWrapperCSSClass ( )
134
+ } , {
117
135
} ) ;
118
136
}
119
137
138
+ /**
139
+ * Allow sub components to stack CSS class names for the wrapper element
140
+ *
141
+ * @return {string }
142
+ * The constructed wrapper DOM `className`
143
+ */
144
+ buildWrapperCSSClass ( ) {
145
+ let menuButtonClass = 'vjs-menu-button' ;
146
+
147
+ // If the inline option is passed, we want to use different styles altogether.
148
+ if ( this . options_ . inline === true ) {
149
+ menuButtonClass += '-inline' ;
150
+ } else {
151
+ menuButtonClass += '-popup' ;
152
+ }
153
+
154
+ // TODO: Fix the CSS so that this isn't necessary
155
+ const buttonClass = Button . prototype . buildCSSClass ( ) ;
156
+
157
+ return `vjs-menu-button ${ menuButtonClass } ${ buttonClass } ${ super . buildCSSClass ( ) } ` ;
158
+ }
159
+
120
160
/**
121
161
* Builds the default DOM `className`.
122
162
*
@@ -163,6 +203,47 @@ class MenuButton extends ClickableComponent {
163
203
}
164
204
}
165
205
206
+ /**
207
+ * Set the focus to the actual button, not to this element
208
+ */
209
+ focus ( ) {
210
+ this . menuButton_ . focus ( ) ;
211
+ }
212
+
213
+ /**
214
+ * Remove the focus from the actual button, not this element
215
+ */
216
+ blur ( ) {
217
+ this . menuButton_ . blur ( ) ;
218
+ }
219
+
220
+ /**
221
+ * This gets called when a `MenuButton` gains focus via a `focus` event.
222
+ * Turns on listening for `keydown` events. When they happen it
223
+ * calls `this.handleKeyPress`.
224
+ *
225
+ * @param {EventTarget~Event } event
226
+ * The `focus` event that caused this function to be called.
227
+ *
228
+ * @listens focus
229
+ */
230
+ handleFocus ( ) {
231
+ Events . on ( document , 'keydown' , Fn . bind ( this , this . handleKeyPress ) ) ;
232
+ }
233
+
234
+ /**
235
+ * Called when a `MenuButton` loses focus. Turns off the listener for
236
+ * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
237
+ *
238
+ * @param {EventTarget~Event } event
239
+ * The `blur` event that caused this function to be called.
240
+ *
241
+ * @listens blur
242
+ */
243
+ handleBlur ( ) {
244
+ Events . off ( document , 'keydown' , Fn . bind ( this , this . handleKeyPress ) ) ;
245
+ }
246
+
166
247
/**
167
248
* Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
168
249
* {@link ClickableComponent#handleKeyPress} for instances where this is called.
@@ -182,15 +263,15 @@ class MenuButton extends ClickableComponent {
182
263
// Don't preventDefault for Tab key - we still want to lose focus
183
264
if ( event . which !== 9 ) {
184
265
event . preventDefault ( ) ;
266
+ // Set focus back to the menu button's button
267
+ this . menuButton_ . el_ . focus ( ) ;
185
268
}
186
269
// Up (38) key or Down (40) key press the 'button'
187
270
} else if ( event . which === 38 || event . which === 40 ) {
188
271
if ( ! this . buttonPressed_ ) {
189
272
this . pressButton ( ) ;
190
273
event . preventDefault ( ) ;
191
274
}
192
- } else {
193
- super . handleKeyPress ( event ) ;
194
275
}
195
276
}
196
277
@@ -213,6 +294,8 @@ class MenuButton extends ClickableComponent {
213
294
// Don't preventDefault for Tab key - we still want to lose focus
214
295
if ( event . which !== 9 ) {
215
296
event . preventDefault ( ) ;
297
+ // Set focus back to the menu button's button
298
+ this . menuButton_ . el_ . focus ( ) ;
216
299
}
217
300
}
218
301
}
@@ -224,7 +307,7 @@ class MenuButton extends ClickableComponent {
224
307
if ( this . enabled_ ) {
225
308
this . buttonPressed_ = true ;
226
309
this . menu . lockShowing ( ) ;
227
- this . el_ . setAttribute ( 'aria-expanded' , 'true' ) ;
310
+ this . menuButton_ . el_ . setAttribute ( 'aria-expanded' , 'true' ) ;
228
311
// set the focus into the submenu
229
312
this . menu . focus ( ) ;
230
313
}
@@ -237,32 +320,30 @@ class MenuButton extends ClickableComponent {
237
320
if ( this . enabled_ ) {
238
321
this . buttonPressed_ = false ;
239
322
this . menu . unlockShowing ( ) ;
240
- this . el_ . setAttribute ( 'aria-expanded' , 'false' ) ;
241
- // Set focus back to this menu button
242
- this . el_ . focus ( ) ;
323
+ this . menuButton_ . el_ . setAttribute ( 'aria-expanded' , 'false' ) ;
243
324
}
244
325
}
245
326
246
327
/**
247
328
* Disable the `MenuButton`. Don't allow it to be clicked.
248
329
*/
249
330
disable ( ) {
250
- // Unpress, but don't force focus on this button
251
- this . buttonPressed_ = false ;
252
- this . menu . unlockShowing ( ) ;
253
- this . el_ . setAttribute ( 'aria-expanded' , 'false' ) ;
331
+ this . unpressButton ( ) ;
254
332
255
333
this . enabled_ = false ;
334
+ this . addClass ( 'vjs-disabled' ) ;
256
335
257
- super . disable ( ) ;
336
+ this . menuButton_ . disable ( ) ;
258
337
}
259
338
260
339
/**
261
340
* Enable the `MenuButton`. Allow it to be clicked.
262
341
*/
263
342
enable ( ) {
264
343
this . enabled_ = true ;
265
- super . enable ( ) ;
344
+ this . removeClass ( 'vjs-disabled' ) ;
345
+
346
+ this . menuButton_ . enable ( ) ;
266
347
}
267
348
}
268
349
0 commit comments