mirror of
https://github.com/videojs/video.js.git
synced 2025-04-11 11:42:08 +02:00
feat(volume panel): accessibly volume control (#3957)
Internally uses the VolumeControl and MuteToggle components to create the VolumePanel component. Acts like the old VolumeMenuButton but does not have the screen reader issues of the older version. BREAKING CHANGE: remove VolumeMenuButton, introduce a new default volume control: VolumePanel.
This commit is contained in:
parent
1ba1f5aabd
commit
524f868e31
@ -10,12 +10,13 @@ The architecture of the Video.js player is centered around components. The `Play
|
||||
* [Basic Example](#basic-example)
|
||||
* [Using Options](#using-options)
|
||||
* [Event Listening](#event-listening)
|
||||
* [using on](#using-on)
|
||||
* [Using on](#using-on)
|
||||
* [Using off](#using-off)
|
||||
* [Using one](#using-one)
|
||||
* [Using trigger](#using-trigger)
|
||||
* [Default Component Tree](#default-component-tree)
|
||||
* [Specific Component Details](#specific-component-details)
|
||||
* [Volume Panel](#volume-panel)
|
||||
* [Progress Control](#progress-control)
|
||||
* [Text Track Settings](#text-track-settings)
|
||||
|
||||
@ -316,6 +317,22 @@ Player
|
||||
|
||||
## Specific Component Details
|
||||
|
||||
### Volume Panel
|
||||
|
||||
The `VolumePanel` includes the `MuteToggle` and the `VolumeControl` Components, which will be hidden if volume changes are not supported. There is one important option for the `VolumePanel` which can make your `VolumeControl` appear vertically over the `MuteToggle`. This can be set by passing `VolumePanel` `{inline: false}` as the default behavior is a horizontal `VolumeControl` with `{inline: true}`.
|
||||
|
||||
Example of a vertical `VolumeControl`
|
||||
|
||||
```js
|
||||
let player = videojs('myplayer', {
|
||||
controlBar: {
|
||||
volumePanel: {
|
||||
inline: false
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Text Track Settings
|
||||
|
||||
The text track settings component is only available when using emulated text tracks.
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
.vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
|
||||
.vjs-playback-rate, .vjs-progress-control,
|
||||
.vjs-mute-control, .vjs-volume-control, .vjs-volume-menu-button,
|
||||
.vjs-mute-control, .vjs-volume-control,
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button, .vjs-audio-button { display: none; }
|
||||
}
|
||||
@ -19,7 +19,7 @@
|
||||
.video-js.vjs-layout-x-small:not(.vjs-fullscreen) {
|
||||
.vjs-current-time, .vjs-time-divider, .vjs-duration, .vjs-remaining-time,
|
||||
.vjs-playback-rate,
|
||||
.vjs-mute-control, .vjs-volume-control, .vjs-volume-menu-button,
|
||||
.vjs-mute-control, .vjs-volume-control,
|
||||
.vjs-chapters-button, .vjs-descriptions-button, .vjs-captions-button,
|
||||
.vjs-subtitles-button, .vjs-audio-button { display: none; }
|
||||
}
|
||||
|
@ -35,3 +35,28 @@
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// visually hide a control-bar component without changing the position
|
||||
// and without hiding it from screen readers. This also preserves
|
||||
// a transition animation.
|
||||
.video-js .vjs-control .vjs-visual-hide-vertical {
|
||||
height: 1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
// visually hide a control-bar component without changing the position
|
||||
// and without hiding it from screen readers. This also preserves
|
||||
// a transition animation.
|
||||
.video-js .vjs-control .vjs-visual-hide-horizontal {
|
||||
width: 1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,25 +1,30 @@
|
||||
.video-js .vjs-mute-control,
|
||||
.video-js .vjs-volume-menu-button {
|
||||
.video-js .vjs-mute-control {
|
||||
cursor: pointer;
|
||||
@include flex(none);
|
||||
@extend .vjs-icon-volume-high;
|
||||
}
|
||||
|
||||
.video-js .vjs-mute-control.vjs-vol-0,
|
||||
.video-js .vjs-volume-menu-button.vjs-vol-0 {
|
||||
.video-js .vjs-mute-control.vjs-vol-0 {
|
||||
@extend .vjs-icon-volume-mute;
|
||||
}
|
||||
.video-js .vjs-mute-control.vjs-vol-1,
|
||||
.video-js .vjs-volume-menu-button.vjs-vol-1 {
|
||||
.video-js .vjs-mute-control.vjs-vol-1 {
|
||||
@extend .vjs-icon-volume-low;
|
||||
}
|
||||
.video-js .vjs-mute-control.vjs-vol-2,
|
||||
.video-js .vjs-volume-menu-button.vjs-vol-2 {
|
||||
.video-js .vjs-mute-control.vjs-vol-2 {
|
||||
@extend .vjs-icon-volume-mid;
|
||||
}
|
||||
|
||||
|
||||
.video-js .vjs-volume-control {
|
||||
width: 5em;
|
||||
width: auto;
|
||||
margin-right: 1em;
|
||||
@include flex(none);
|
||||
@include display-flex(center);
|
||||
@include transition(all 0.4s);
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel {
|
||||
width: auto;
|
||||
@include flex(none);
|
||||
@include display-flex(center);
|
||||
}
|
||||
@ -74,6 +79,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.video-js .vjs-volume-panel.vjs-volume-panel-vertical {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
// Assumes volume starts at 1.0.
|
||||
.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level {
|
||||
height: 100%;
|
||||
@ -83,53 +92,15 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// The volume menu button is like menu buttons (captions/subtitles) but works
|
||||
// a little differently. It needs to be possible to tab to the volume slider
|
||||
// without hitting space bar on the menu button. To do this we're not using
|
||||
// display:none to hide the slider menu by default, and instead setting the
|
||||
// width and height to zero.
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button .vjs-menu {
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top-color: transparent;
|
||||
.video-js .vjs-volume-vertical {
|
||||
width: 2.9em;
|
||||
height: 8em;
|
||||
bottom: 5.5em;
|
||||
left: -3.5em;
|
||||
|
||||
@include background-color-with-alpha($primary-background-color, $primary-background-transparency);
|
||||
}
|
||||
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button-vertical .vjs-menu {
|
||||
left: 0.5em;
|
||||
height: 8em;
|
||||
}
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button-horizontal .vjs-menu {
|
||||
.video-js .vjs-volume-horizontal .vjs-menu {
|
||||
left: -2em;
|
||||
}
|
||||
|
||||
.vjs-menu-button-popup.vjs-volume-menu-button .vjs-menu-content {
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
||||
// Avoids unnecessary scrollbars in the menu content. Primarily noticed in Chrome on Linux.
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.vjs-volume-menu-button-vertical:hover .vjs-menu-content,
|
||||
.vjs-volume-menu-button-vertical:focus .vjs-menu-content,
|
||||
.vjs-volume-menu-button-vertical.vjs-slider-active .vjs-menu-content,
|
||||
.vjs-volume-menu-button-vertical .vjs-lock-showing .vjs-menu-content {
|
||||
height: 8em;
|
||||
width: 2.9em;
|
||||
}
|
||||
|
||||
.vjs-volume-menu-button-horizontal:hover .vjs-menu-content,
|
||||
.vjs-volume-menu-button-horizontal:focus .vjs-menu-content,
|
||||
.vjs-volume-menu-button-horizontal .vjs-slider-active .vjs-menu-content,
|
||||
.vjs-volume-menu-button-horizontal .vjs-lock-showing .vjs-menu-content {
|
||||
height: 2.9em;
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.vjs-volume-menu-button.vjs-menu-button-inline .vjs-menu-content {
|
||||
// An inline volume should never have a menu background color.
|
||||
// This protects it from external changes to background colors.
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
@ -18,11 +18,6 @@
|
||||
// This width is currently specific to the inline volume bar.
|
||||
width: 12em;
|
||||
}
|
||||
// Don't transition when tabbing in reverse to the volume menu
|
||||
// because it looks weird
|
||||
.video-js .vjs-menu-button-inline.vjs-slider-active {
|
||||
@include transition(none);
|
||||
}
|
||||
|
||||
.vjs-menu-button-inline .vjs-menu {
|
||||
opacity: 0;
|
||||
|
@ -12,9 +12,7 @@ import './time-controls/remaining-time-display.js';
|
||||
import './live-display.js';
|
||||
import './progress-control/progress-control.js';
|
||||
import './fullscreen-toggle.js';
|
||||
import './volume-control/volume-control.js';
|
||||
import './volume-menu-button.js';
|
||||
import './mute-toggle.js';
|
||||
import './volume-panel.js';
|
||||
import './text-track-controls/chapters-button.js';
|
||||
import './text-track-controls/descriptions-button.js';
|
||||
import './text-track-controls/subtitles-button.js';
|
||||
@ -56,7 +54,7 @@ class ControlBar extends Component {
|
||||
ControlBar.prototype.options_ = {
|
||||
children: [
|
||||
'playToggle',
|
||||
'volumeMenuButton',
|
||||
'volumePanel',
|
||||
'currentTimeDisplay',
|
||||
'timeDivider',
|
||||
'durationDisplay',
|
||||
|
@ -4,6 +4,7 @@
|
||||
import Button from '../button';
|
||||
import Component from '../component';
|
||||
import * as Dom from '../utils/dom.js';
|
||||
import checkVolumeSupport from './volume-control/check-volume-support';
|
||||
|
||||
/**
|
||||
* A button component for muting the audio.
|
||||
@ -24,23 +25,10 @@ class MuteToggle extends Button {
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'volumechange', this.update);
|
||||
// hide this control if volume support is missing
|
||||
checkVolumeSupport(this, player);
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
|
||||
this.on(player, 'loadstart', function() {
|
||||
// We need to update the button to account for a default muted state.
|
||||
this.update();
|
||||
|
||||
if (player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
}
|
||||
});
|
||||
this.on(player, ['loadstart', 'volumechange'], this.update);
|
||||
}
|
||||
|
||||
/**
|
||||
|
28
src/js/control-bar/volume-control/check-volume-support.js
Normal file
28
src/js/control-bar/volume-control/check-volume-support.js
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Check if volume control is supported and if it isn't hide the
|
||||
* `Component` that was passed using the `vjs-hidden` class.
|
||||
*
|
||||
* @param {Component} self
|
||||
* The component that should be hidden if volume is unsupported
|
||||
*
|
||||
* @param {Player} player
|
||||
* A reference to the player
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const checkVolumeSupport = function(self, player) {
|
||||
// hide volume controls when they're not supported by the current tech
|
||||
if (player.tech_ && !player.tech_.featuresVolumeControl) {
|
||||
self.addClass('vjs-hidden');
|
||||
}
|
||||
|
||||
self.on(player, 'loadstart', function() {
|
||||
if (!player.tech_.featuresVolumeControl) {
|
||||
self.addClass('vjs-hidden');
|
||||
} else {
|
||||
self.removeClass('vjs-hidden');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default checkVolumeSupport;
|
@ -3,7 +3,6 @@
|
||||
*/
|
||||
import Slider from '../../slider/slider.js';
|
||||
import Component from '../../component.js';
|
||||
import * as Fn from '../../utils/fn.js';
|
||||
|
||||
// Required children
|
||||
import './volume-level.js';
|
||||
@ -26,8 +25,9 @@ class VolumeBar extends Slider {
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
|
||||
this.on(player, 'volumechange', this.updateARIAAttributes);
|
||||
player.ready(Fn.bind(this, this.updateARIAAttributes));
|
||||
player.ready(() => this.updateARIAAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,7 +40,8 @@ class VolumeBar extends Slider {
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-volume-bar vjs-slider-bar'
|
||||
}, {
|
||||
'aria-label': 'volume level'
|
||||
'aria-label': 'volume level',
|
||||
'aria-live': 'polite'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
* @file volume-control.js
|
||||
*/
|
||||
import Component from '../../component.js';
|
||||
import checkVolumeSupport from './check-volume-support';
|
||||
import {isPlain} from '../../utils/obj';
|
||||
|
||||
// Required children
|
||||
import './volume-bar.js';
|
||||
@ -22,20 +24,81 @@ class VolumeControl extends Component {
|
||||
* @param {Object} [options={}]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
constructor(player, options = {}) {
|
||||
options.vertical = options.vertical || false;
|
||||
|
||||
// Pass the vertical option down to the VolumeBar if
|
||||
// the VolumeBar is turned on.
|
||||
if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
|
||||
options.volumeBar = options.volumeBar || {};
|
||||
options.volumeBar.vertical = options.vertical;
|
||||
}
|
||||
|
||||
super(player, options);
|
||||
|
||||
// hide volume controls when they're not supported by the current tech
|
||||
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
}
|
||||
this.on(player, 'loadstart', function() {
|
||||
if (player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
// hide this control if volume support is missing
|
||||
checkVolumeSupport(this, player);
|
||||
|
||||
// while the slider is active (the mouse has been pressed down and
|
||||
// is dragging) we do not want to hide the VolumeBar
|
||||
this.on(this.volumeBar, ['slideractive'], () => {
|
||||
this.volumeBar.addClass('vjs-slider-active');
|
||||
this.lockShowing_ = true;
|
||||
});
|
||||
|
||||
// when the slider becomes inactive again we want to hide
|
||||
// the VolumeBar, but only if we tried to hide when
|
||||
// lockShowing_ was true. see the VolumeBar#hide function.
|
||||
this.on(this.volumeBar, ['sliderinactive'], () => {
|
||||
this.volumeBar.removeClass('vjs-slider-active');
|
||||
this.lockShowing_ = false;
|
||||
|
||||
if (this.shouldHide_) {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// show/hide the VolumeBar on focus/blur
|
||||
// happens in VolumeControl but if we want to use the
|
||||
// VolumeBar by itself we will need this
|
||||
this.on(this.volumeBar, ['focus'], () => this.show());
|
||||
this.on(this.volumeBar, ['blur'], () => this.hide());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the visual hidden state from the `VolumeControl`.
|
||||
*/
|
||||
show() {
|
||||
this.shouldHide_ = false;
|
||||
|
||||
if (this.options_.vertical) {
|
||||
this.removeClass('vjs-visual-hide-vertical');
|
||||
} else {
|
||||
this.removeClass('vjs-visual-hide-horizontal');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the `VolumeControl` visually but not from screen-readers unless
|
||||
* showing is locked (due to the slider being active). If showing is locked
|
||||
* hide will be called when the slider becomes inactive.
|
||||
*/
|
||||
hide() {
|
||||
// if we are currently locked to the showing state
|
||||
// don't hide, but store that we should hide when
|
||||
// lockShowing_ turns to a false value.
|
||||
if (this.lockShowing_) {
|
||||
this.shouldHide_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// animate hiding the bar via transitions
|
||||
if (this.options_.vertical) {
|
||||
this.addClass('vjs-visual-hide-vertical');
|
||||
} else {
|
||||
this.addClass('vjs-visual-hide-horizontal');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,8 +108,14 @@ class VolumeControl extends Component {
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
let orientationClass = 'vjs-volume-horizonal vjs-visual-hide-horizontal';
|
||||
|
||||
if (this.options_.vertical) {
|
||||
orientationClass = 'vjs-volume-vertical vjs-visual-hide-vertical';
|
||||
}
|
||||
|
||||
return super.createEl('div', {
|
||||
className: 'vjs-volume-control vjs-control'
|
||||
className: `vjs-volume-control vjs-control ${orientationClass}`
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,190 +0,0 @@
|
||||
/**
|
||||
* @file volume-menu-button.js
|
||||
*/
|
||||
import * as Fn from '../utils/fn.js';
|
||||
import Component from '../component.js';
|
||||
import Popup from '../popup/popup.js';
|
||||
import PopupButton from '../popup/popup-button.js';
|
||||
import MuteToggle from './mute-toggle.js';
|
||||
import VolumeBar from './volume-control/volume-bar.js';
|
||||
|
||||
/**
|
||||
* Button for volume popup
|
||||
*
|
||||
* @extends PopupButton
|
||||
*/
|
||||
class VolumeMenuButton extends PopupButton {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options={}]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options = {}) {
|
||||
// Default to inline
|
||||
if (options.inline === undefined) {
|
||||
options.inline = true;
|
||||
}
|
||||
|
||||
// If the vertical option isn't passed at all, default to true.
|
||||
if (options.vertical === undefined) {
|
||||
// If an inline volumeMenuButton is used, we should default to using
|
||||
// a horizontal slider for obvious reasons.
|
||||
if (options.inline) {
|
||||
options.vertical = false;
|
||||
} else {
|
||||
options.vertical = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The vertical option needs to be set on the volumeBar as well,
|
||||
// since that will need to be passed along to the VolumeBar constructor
|
||||
options.volumeBar = options.volumeBar || {};
|
||||
options.volumeBar.vertical = !!options.vertical;
|
||||
|
||||
super(player, options);
|
||||
|
||||
// Same listeners as MuteToggle
|
||||
this.on(player, 'volumechange', this.volumeUpdate);
|
||||
this.on(player, 'loadstart', this.volumeUpdate);
|
||||
|
||||
// hide mute toggle if the current tech doesn't support volume control
|
||||
function updateVisibility() {
|
||||
if (player.tech_ && player.tech_.featuresVolumeControl === false) {
|
||||
this.addClass('vjs-hidden');
|
||||
} else {
|
||||
this.removeClass('vjs-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
updateVisibility.call(this);
|
||||
this.on(player, 'loadstart', updateVisibility);
|
||||
|
||||
this.on(this.volumeBar, ['slideractive', 'focus'], function() {
|
||||
this.addClass('vjs-slider-active');
|
||||
});
|
||||
|
||||
this.on(this.volumeBar, ['sliderinactive', 'blur'], function() {
|
||||
this.removeClass('vjs-slider-active');
|
||||
});
|
||||
|
||||
this.on(this.volumeBar, ['focus'], function() {
|
||||
this.addClass('vjs-lock-showing');
|
||||
});
|
||||
|
||||
this.on(this.volumeBar, ['blur'], function() {
|
||||
this.removeClass('vjs-lock-showing');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the default DOM `className`.
|
||||
*
|
||||
* @return {string}
|
||||
* The DOM `className` for this object.
|
||||
*/
|
||||
buildCSSClass() {
|
||||
let orientationClass = '';
|
||||
|
||||
if (this.options_.vertical) {
|
||||
orientationClass = 'vjs-volume-menu-button-vertical';
|
||||
} else {
|
||||
orientationClass = 'vjs-volume-menu-button-horizontal';
|
||||
}
|
||||
|
||||
return `vjs-volume-menu-button ${super.buildCSSClass()} ${orientationClass}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the VolumeMenuButton popup
|
||||
*
|
||||
* @return {Popup}
|
||||
* The popup that was created
|
||||
*/
|
||||
createPopup() {
|
||||
const popup = new Popup(this.player_, {
|
||||
contentElType: 'div'
|
||||
});
|
||||
|
||||
const vb = new VolumeBar(this.player_, this.options_.volumeBar);
|
||||
|
||||
popup.addChild(vb);
|
||||
|
||||
this.menuContent = popup;
|
||||
this.volumeBar = vb;
|
||||
|
||||
this.attachVolumeBarEvents();
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called when an `VolumeMenuButton` is "clicked". See
|
||||
* {@link ClickableComponent} for more detailed information on what a click can be.
|
||||
*
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `keydown`, `tap`, or `click` event that caused this function to be
|
||||
* called.
|
||||
*
|
||||
* @listens tap
|
||||
* @listens click
|
||||
*/
|
||||
handleClick(event) {
|
||||
MuteToggle.prototype.handleClick.call(this);
|
||||
super.handleClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add events listeners to the created `VolumeBar`.
|
||||
*/
|
||||
attachVolumeBarEvents() {
|
||||
this.menuContent.on(['mousedown', 'touchdown'], Fn.bind(this, this.handleMouseDown));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the `mousedown` and `touchdown` events on the `VolumeBar`
|
||||
*
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `mousedown` or `touchdown` event that caused this to run.
|
||||
*
|
||||
* @listens mousedown
|
||||
* @listens touchdown
|
||||
*/
|
||||
handleMouseDown(event) {
|
||||
this.on(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove));
|
||||
this.on(this.el_.ownerDocument, ['mouseup', 'touchend'], this.handleMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the `mouseup` and `touchend` events on the `VolumeBar`
|
||||
*
|
||||
* @param {EventTarget~Event} [event]
|
||||
* The `mouseup` or `touchend` event that caused this to run.
|
||||
*
|
||||
* @listens mouseup
|
||||
* @listens touchend
|
||||
*/
|
||||
handleMouseUp(event) {
|
||||
this.off(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @borrows MuteToggle#update as VolumeMenuButton#volumeUpdate
|
||||
*/
|
||||
VolumeMenuButton.prototype.volumeUpdate = MuteToggle.prototype.update;
|
||||
|
||||
/**
|
||||
* The text that should display over the `VolumeMenuButton`s controls. Added for localization.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
VolumeMenuButton.prototype.controlText_ = 'Mute';
|
||||
|
||||
Component.registerComponent('VolumeMenuButton', VolumeMenuButton);
|
||||
export default VolumeMenuButton;
|
94
src/js/control-bar/volume-panel.js
Normal file
94
src/js/control-bar/volume-panel.js
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @file volume-control.js
|
||||
*/
|
||||
import Component from '../component.js';
|
||||
import checkVolumeSupport from './volume-control/check-volume-support';
|
||||
import {isPlain} from '../utils/obj';
|
||||
|
||||
// Required children
|
||||
import './volume-control/volume-control.js';
|
||||
import './mute-toggle.js';
|
||||
|
||||
/**
|
||||
* A Component to contain the MuteToggle and VolumeControl so that
|
||||
* they can work together.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class VolumePanel extends Component {
|
||||
|
||||
/**
|
||||
* Creates an instance of this class.
|
||||
*
|
||||
* @param {Player} player
|
||||
* The `Player` that this class should be attached to.
|
||||
*
|
||||
* @param {Object} [options={}]
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options = {}) {
|
||||
if (typeof options.inline !== 'undefined') {
|
||||
options.inline = options.inline;
|
||||
} else {
|
||||
options.inline = true;
|
||||
}
|
||||
|
||||
// pass the inline option down to the VolumeControl as vertical if
|
||||
// the VolumeControl is on.
|
||||
if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
|
||||
options.volumeControl = options.volumeControl || {};
|
||||
options.volumeControl.vertical = !options.inline;
|
||||
}
|
||||
|
||||
super(player, options);
|
||||
|
||||
// hide this control if volume support is missing
|
||||
checkVolumeSupport(this, player);
|
||||
|
||||
// when the mouse leaves the VolumePanel area hide the VolumeControl (slider/bar)
|
||||
this.on(['mouseenter', 'touchstart'], () => this.volumeControl.show());
|
||||
this.on(['mouseleave', 'touchend'], () => this.volumeControl.hide());
|
||||
|
||||
// when any child of the VolumePanel gets or loses focus
|
||||
// show/hide the VolumeControl (slider/bar)
|
||||
this.children().forEach((child) => {
|
||||
this.on(child, ['focus'], () => this.volumeControl.show());
|
||||
this.on(child, ['blur'], () => this.volumeControl.hide());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `Component`'s DOM element
|
||||
*
|
||||
* @return {Element}
|
||||
* The element that was created.
|
||||
*/
|
||||
createEl() {
|
||||
let orientationClass = 'vjs-volume-panel-horizontal';
|
||||
|
||||
if (!this.options_.inline) {
|
||||
orientationClass = 'vjs-volume-panel-vertical';
|
||||
}
|
||||
|
||||
return super.createEl('div', {
|
||||
className: `vjs-volume-panel vjs-control ${orientationClass}`
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Default options for the `VolumeControl`
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
VolumePanel.prototype.options_ = {
|
||||
children: [
|
||||
'muteToggle',
|
||||
'volumeControl'
|
||||
]
|
||||
};
|
||||
|
||||
Component.registerComponent('VolumePanel', VolumePanel);
|
||||
export default VolumePanel;
|
Loading…
x
Reference in New Issue
Block a user