1
0
mirror of https://github.com/videojs/video.js.git synced 2025-02-14 12:20:48 +02:00

fix: make 'Esc' works for a vertical volume bar and menus (#6046)

Improve player accessibility by adding 'Esc' functionality to the volume panel and menu popups.

Fixes #6004.
This commit is contained in:
Grzegorz Blaszczyk 2019-08-21 20:24:35 +02:00 committed by Gary Katsevman
parent 51b9861d5b
commit 62465b841e
5 changed files with 128 additions and 9 deletions

View File

@ -32,35 +32,33 @@
width: 1px; width: 1px;
height: 1px; height: 1px;
margin-left: -1px; margin-left: -1px;
} }
.video-js .vjs-volume-panel { .video-js .vjs-volume-panel {
&:hover .vjs-volume-control, &.vjs-hover .vjs-volume-control,
&:active .vjs-volume-control, &:active .vjs-volume-control,
&:focus .vjs-volume-control, &:focus .vjs-volume-control,
& .vjs-volume-control:hover , & .vjs-volume-control:active,
& .vjs-volume-control:active , &.vjs-hover .vjs-mute-control ~ .vjs-volume-control,
& .vjs-mute-control:hover ~ .vjs-volume-control,
& .vjs-volume-control.vjs-slider-active { & .vjs-volume-control.vjs-slider-active {
&.vjs-volume-horizontal { &.vjs-volume-horizontal {
width: 5em; width: 5em;
height: 3em; height: 3em;
} }
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
position: relative; position: relative;
&.vjs-volume-vertical { &.vjs-volume-vertical {
left: -3.5em; left: -3.5em;
@include transition(left 0s);
} }
$transition-property: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s; $transition-property: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s;
@include transition($transition-property); @include transition($transition-property);
} }
&.vjs-volume-panel-horizontal { &.vjs-volume-panel-horizontal {
&:hover, &.vjs-hover,
&:active, &:active,
&.vjs-slider-active { &.vjs-slider-active {
width: 9em; width: 9em;
@ -83,6 +81,7 @@
$transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s; $transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s;
@include transition($transition-property) @include transition($transition-property)
} }
.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal { .video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal {
$transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s; $transition-property: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s;
@include transition($transition-property) @include transition($transition-property)

View File

@ -38,7 +38,7 @@
max-height: 25em; max-height: 25em;
} }
.vjs-workinghover .vjs-menu-button-popup:hover .vjs-menu, .vjs-workinghover .vjs-menu-button-popup.vjs-hover .vjs-menu,
.vjs-menu-button-popup .vjs-menu.vjs-lock-showing { .vjs-menu-button-popup .vjs-menu.vjs-lock-showing {
display: block; display: block;
} }

View File

@ -168,6 +168,8 @@ class ClickableComponent extends Component {
if (typeof this.tabIndex_ !== 'undefined') { if (typeof this.tabIndex_ !== 'undefined') {
this.el_.removeAttribute('tabIndex'); this.el_.removeAttribute('tabIndex');
} }
this.off('mouseover', this.handleMouseOver);
this.off('mouseout', this.handleMouseOut);
this.off(['tap', 'click'], this.handleClick); this.off(['tap', 'click'], this.handleClick);
this.off('keydown', this.handleKeyDown); this.off('keydown', this.handleKeyDown);
} }

View File

@ -3,6 +3,10 @@
*/ */
import Component from '../component.js'; import Component from '../component.js';
import {isPlain} from '../utils/obj'; import {isPlain} from '../utils/obj';
import * as Events from '../utils/events.js';
import * as Fn from '../utils/fn.js';
import keycode from 'keycode';
import document from 'global/document';
// Required children // Required children
import './volume-control/volume-control.js'; import './volume-control/volume-control.js';
@ -42,6 +46,11 @@ class VolumePanel extends Component {
super(player, options); super(player, options);
this.on(player, ['loadstart'], this.volumePanelState_); this.on(player, ['loadstart'], this.volumePanelState_);
this.on(this.muteToggle, 'keyup', this.handleKeyPress);
this.on(this.volumeControl, 'keyup', this.handleVolumeControlKeyUp);
this.on('keydown', this.handleKeyPress);
this.on('mouseover', this.handleMouseOver);
this.on('mouseout', this.handleMouseOut);
// while the slider is active (the mouse has been pressed down and // while the slider is active (the mouse has been pressed down and
// is dragging) we do not want to hide the VolumeBar // is dragging) we do not want to hide the VolumeBar
@ -109,6 +118,73 @@ class VolumePanel extends Component {
}); });
} }
/**
* Dispose of the `volume-panel` and all child components.
*/
dispose() {
this.handleMouseOut();
super.dispose();
}
/**
* Handles `keyup` events on the `VolumeControl`, looking for ESC, which closes
* the volume panel and sets focus on `MuteToggle`.
*
* @param {EventTarget~Event} event
* The `keyup` event that caused this function to be called.
*
* @listens keyup
*/
handleVolumeControlKeyUp(event) {
if (keycode.isEventKey(event, 'Esc')) {
this.muteToggle.focus();
}
}
/**
* This gets called when a `VolumePanel` gains hover via a `mouseover` event.
* Turns on listening for `mouseover` event. When they happen it
* calls `this.handleMouseOver`.
*
* @param {EventTarget~Event} event
* The `mouseover` event that caused this function to be called.
*
* @listens mouseover
*/
handleMouseOver(event) {
this.addClass('vjs-hover');
Events.on(document, 'keyup', Fn.bind(this, this.handleKeyPress));
}
/**
* This gets called when a `VolumePanel` gains hover via a `mouseout` event.
* Turns on listening for `mouseout` event. When they happen it
* calls `this.handleMouseOut`.
*
* @param {EventTarget~Event} event
* The `mouseout` event that caused this function to be called.
*
* @listens mouseout
*/
handleMouseOut(event) {
this.removeClass('vjs-hover');
Events.off(document, 'keyup', Fn.bind(this, this.handleKeyPress));
}
/**
* Handles `keyup` event on the document or `keydown` event on the `VolumePanel`,
* looking for ESC, which hides the `VolumeControl`.
*
* @param {EventTarget~Event} event
* The keypress that triggered this event.
*
* @listens keydown | keyup
*/
handleKeyPress(event) {
if (keycode.isEventKey(event, 'Esc')) {
this.handleMouseOut();
}
}
} }
/** /**

View File

@ -5,8 +5,11 @@ import Button from '../button.js';
import Component from '../component.js'; import Component from '../component.js';
import Menu from './menu.js'; import Menu from './menu.js';
import * as Dom from '../utils/dom.js'; import * as Dom from '../utils/dom.js';
import * as Fn from '../utils/fn.js';
import * as Events from '../utils/events.js';
import {toTitleCase} from '../utils/string-cases.js'; import {toTitleCase} from '../utils/string-cases.js';
import { IS_IOS } from '../utils/browser.js'; import { IS_IOS } from '../utils/browser.js';
import document from 'global/document';
import keycode from 'keycode'; import keycode from 'keycode';
/** /**
@ -49,9 +52,11 @@ class MenuButton extends Component {
this.on(this.menuButton_, 'click', this.handleClick); this.on(this.menuButton_, 'click', this.handleClick);
this.on(this.menuButton_, 'keydown', this.handleKeyDown); this.on(this.menuButton_, 'keydown', this.handleKeyDown);
this.on(this.menuButton_, 'mouseenter', () => { this.on(this.menuButton_, 'mouseenter', () => {
this.addClass('vjs-hover');
this.menu.show(); this.menu.show();
Events.on(document, 'keyup', Fn.bind(this, this.handleMenuKeyUp));
}); });
this.on('mouseleave', this.handleMouseLeave);
this.on('keydown', this.handleSubmenuKeyDown); this.on('keydown', this.handleSubmenuKeyDown);
} }
@ -210,6 +215,14 @@ class MenuButton extends Component {
return this.menuButton_.controlText(text, el); return this.menuButton_.controlText(text, el);
} }
/**
* Dispose of the `menu-button` and all child components.
*/
dispose() {
this.handleMouseLeave();
super.dispose();
}
/** /**
* Handle a click on a `MenuButton`. * Handle a click on a `MenuButton`.
* See {@link ClickableComponent#handleClick} for instances where this is called. * See {@link ClickableComponent#handleClick} for instances where this is called.
@ -229,6 +242,19 @@ class MenuButton extends Component {
} }
} }
/**
* Handle `mouseleave` for `MenuButton`.
*
* @param {EventTarget~Event} event
* The `mouseleave` event that caused this function to be called.
*
* @listens mouseleave
*/
handleMouseLeave(event) {
this.removeClass('vjs-hover');
Events.off(document, 'keyup', Fn.bind(this, this.handleMenuKeyUp));
}
/** /**
* Set the focus to the actual button, not to this element * Set the focus to the actual button, not to this element
*/ */
@ -275,6 +301,22 @@ class MenuButton extends Component {
} }
} }
/**
* Handle a `keyup` event on a `MenuButton`. The listener for this is added in
* the constructor.
*
* @param {EventTarget~Event} event
* Key press event
*
* @listens keyup
*/
handleMenuKeyUp(event) {
// Escape hides popup menu
if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
this.removeClass('vjs-hover');
}
}
/** /**
* This method name now delegates to `handleSubmenuKeyDown`. This means * This method name now delegates to `handleSubmenuKeyDown`. This means
* anyone calling `handleSubmenuKeyPress` will not see their method calls * anyone calling `handleSubmenuKeyPress` will not see their method calls