Playback-rate: fix popup behavior (#1650)
* Playback-rate: fix popup behavior - Part of 1637 - invokes the popup menu when clicked. - This also makes the button consistent with other `MenuButton`s, i.e. to invoke a menu popup when clicked instead of hovered. * Adjust CSS Co-authored-by: Raphael Wickihalder <raphael.wickihalder@odysee.com>
This commit is contained in:
parent
d5b7f25191
commit
d3365d69f9
3 changed files with 119 additions and 1 deletions
|
@ -0,0 +1,95 @@
|
||||||
|
import videojs from 'video.js';
|
||||||
|
|
||||||
|
const IS_DEV = process.env.NODE_ENV !== 'production';
|
||||||
|
const CONTROL_BAR = 'ControlBar';
|
||||||
|
const PLAYBACK_RATE_MENU_BUTTON = 'PlaybackRateMenuButton';
|
||||||
|
const MENU = 'Menu';
|
||||||
|
|
||||||
|
const Menu = videojs.getComponent(MENU);
|
||||||
|
const PlaybackRateMenuButton = videojs.getComponent(PLAYBACK_RATE_MENU_BUTTON);
|
||||||
|
|
||||||
|
class LbryPlaybackRateMenuButton extends PlaybackRateMenuButton {
|
||||||
|
static replaceExisting(player) {
|
||||||
|
try {
|
||||||
|
const controlBar = player.getChild(CONTROL_BAR);
|
||||||
|
const playbackRateMenuButton = controlBar.getChild(PLAYBACK_RATE_MENU_BUTTON);
|
||||||
|
controlBar.removeChild(playbackRateMenuButton);
|
||||||
|
controlBar.addChild(new LbryPlaybackRateMenuButton(player));
|
||||||
|
} catch (error) {
|
||||||
|
if (IS_DEV) throw Error('\n\nvideojs.jsx: Playback rate hierarchy changed?\n\n' + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(player, options = {}) {
|
||||||
|
super(player, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default behavior cycles through the selection, while the popup is
|
||||||
|
* invoked by hover. We have removed the hover behavior (1637) since it is
|
||||||
|
* annoying when accidentally hovered, and it's also needed to address a
|
||||||
|
* z-order issue.
|
||||||
|
*
|
||||||
|
* With the hover behavior gone, we need to override the default click
|
||||||
|
* behavior to invoke the popup instead of cycling through the settings.
|
||||||
|
*
|
||||||
|
* Ref: https://github.com/videojs/video.js/issues/3394#issuecomment-230793773
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
handleClick(event) {
|
||||||
|
if (this.buttonPressed_) {
|
||||||
|
this.unpressButton();
|
||||||
|
} else {
|
||||||
|
this.pressButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default menu behavior is to dismiss the popup when losing focus, unless
|
||||||
|
* the new focus is on the associated button. The associated button is
|
||||||
|
* specifically defined as the first child, but PlaybackRateMenuButton
|
||||||
|
* implements it as the second child.
|
||||||
|
*
|
||||||
|
* Fixed by reversing the check to use `relatedTarget.parentElement` instead.
|
||||||
|
* It shouldn't matter which sub-element was clicked, as long as it's part of
|
||||||
|
* the button.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param menu
|
||||||
|
*/
|
||||||
|
handleMenuBlur(event, menu) {
|
||||||
|
const relatedTarget = event.relatedTarget || document.activeElement;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!menu.children().some((element) => {
|
||||||
|
return element.el() === relatedTarget;
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
const btn = menu.menuButton_;
|
||||||
|
if (btn && btn.buttonPressed_ && relatedTarget.parentElement !== btn.el()) {
|
||||||
|
btn.unpressButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createMenu() {
|
||||||
|
const menu = new Menu(this.player_, { menuButton: this });
|
||||||
|
|
||||||
|
// Override the original with ours. Must be done after the constructor
|
||||||
|
// is called, as that is where `boundHandleBlur_` is originally set.
|
||||||
|
menu.boundHandleBlur_ = (e) => this.handleMenuBlur(e, menu);
|
||||||
|
|
||||||
|
this.hideThreshold_ = 0; // Must reset. See `MenuButton::createMenu` notes.
|
||||||
|
this.items = this.createItems();
|
||||||
|
|
||||||
|
if (this.items) {
|
||||||
|
for (let i = 0; i < this.items.length; ++i) {
|
||||||
|
menu.addItem(this.items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LbryPlaybackRateMenuButton;
|
|
@ -16,6 +16,7 @@ import eventTracking from 'videojs-event-tracking';
|
||||||
import functions from './videojs-functions';
|
import functions from './videojs-functions';
|
||||||
import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
|
import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
|
||||||
import keyboardShorcuts from './videojs-shortcuts';
|
import keyboardShorcuts from './videojs-shortcuts';
|
||||||
|
import LbryPlaybackRateMenuButton from './lbry-playback-rate';
|
||||||
import LbryVolumeBarClass from './lbry-volume-bar';
|
import LbryVolumeBarClass from './lbry-volume-bar';
|
||||||
import Chromecast from './chromecast';
|
import Chromecast from './chromecast';
|
||||||
import playerjs from 'player.js';
|
import playerjs from 'player.js';
|
||||||
|
@ -278,8 +279,8 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
|
|
||||||
// runAds(internalFeatureEnabled, allowPreRoll, player, embedded);
|
// runAds(internalFeatureEnabled, allowPreRoll, player, embedded);
|
||||||
|
|
||||||
// Replace volume bar with custom LBRY volume bar
|
|
||||||
LbryVolumeBarClass.replaceExisting(player);
|
LbryVolumeBarClass.replaceExisting(player);
|
||||||
|
LbryPlaybackRateMenuButton.replaceExisting(player);
|
||||||
|
|
||||||
// Add reloadSourceOnError plugin
|
// Add reloadSourceOnError plugin
|
||||||
player.reloadSourceOnError({ errorInterval: 10 });
|
player.reloadSourceOnError({ errorInterval: 10 });
|
||||||
|
|
|
@ -205,6 +205,16 @@ $control-bar-popup-font-size: 0.8rem;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vjs-lock-showing {
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
width: 8em !important;
|
||||||
|
// overflow-y:scroll;
|
||||||
|
.vjs-menu-content {
|
||||||
|
max-height: 176px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[dir] .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
[dir] .vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
||||||
//background-color: rgba(43, 51, 63);
|
//background-color: rgba(43, 51, 63);
|
||||||
background-color: rgba(var(--color-header-button-base), 0.9);
|
background-color: rgba(var(--color-header-button-base), 0.9);
|
||||||
|
@ -239,6 +249,18 @@ $control-bar-popup-font-size: 0.8rem;
|
||||||
background-color: var(--color-primary);
|
background-color: var(--color-primary);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
padding: 0.1em 0;
|
||||||
|
margin-left: var(--spacing-xxs);
|
||||||
|
margin-right: var(--spacing-xxs);
|
||||||
|
&:first-child {
|
||||||
|
margin-top: var(--spacing-xxs);
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: var(--spacing-xxs);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.vjs-selected {
|
.vjs-selected {
|
||||||
background-color: var(--color-primary);
|
background-color: var(--color-primary);
|
||||||
|
|
Loading…
Reference in a new issue