Skip to content

Commit

Permalink
feat: Adds a transient button component
Browse files Browse the repository at this point in the history
  • Loading branch information
mister-ben committed Mar 5, 2024
1 parent b59a89b commit 37e6cc9
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 0 deletions.
102 changes: 102 additions & 0 deletions sandbox/transient-button.html.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Video.js Sandbox</title>
<link href="../dist/video-js.css" rel="stylesheet" type="text/css" />
<script src="../dist/video.js"></script>
</head>
<body>
<video-js
id="vid1"
controls
muted
preload="auto"
width="640"
height="264"
poster="https://vjs.zencdn.net/v/oceans.png"
>
<source src="https://vjs.zencdn.net/v/oceans.mp4" type="video/mp4" />
<source src="https://vjs.zencdn.net/v/oceans.webm" type="video/webm" />
<source src="https://vjs.zencdn.net/v/oceans.ogv" type="video/ogg" />
<track
kind="captions"
src="../docs/examples/shared/example-captions.vtt"
srclang="en"
label="English"
/>
</video-js>

<style>
.vjs-transient-button.unmute-button span::before {
content: "\f104";
font-family: "VideoJS";
}
</style>

<script>
const player = videojs("#vid1");

player.ready(function () {
// Adds an unmute button that umutes and goes away when clicked
player.one("playing", function () {
if (this.muted()) {
const unmuteButton = player.addChild("TransientButton", {
controlText: "Unmute",
position: "top left",
className: "unmute-button",
clickHandler: function () {
this.player().muted(false);
this.dispose();
},
});
unmuteButton.show();
console.log(unmuteButton.el());
}
});

const track = player.addRemoteTextTrack({
src:
"data:text/vtt;base64," +
btoa(`WEBVTT

00:00.000 --> 00:10.000
Recap

00:15.000 --> 00:20.000
Intro

00:40.000 --> 00:47.000
Credits

`),
kind: "metadata",
label: "skip_sections",
}).track;

let skipButtons = [];

track.addEventListener("cuechange", function () {
const cue = track.activeCues[0];
console.log(cue);
if (cue) {
const skipButton = player.addChild("TransientButton", {
controlText: `Skip ${cue.text}`,
position: "bottom right",
clickHandler: () => {
player.currentTime(cue.endTime);
},
});
skipButtons.push(skipButton);
skipButton.show();
} else {
while (skipButtons.length > 0) {
skipButtons.shift().dispose();
}
}
});
track.mode = "hidden";
});
</script>
</body>
</html>
58 changes: 58 additions & 0 deletions src/css/components/_transient-button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.video-js .vjs-transient-button {
position: absolute;
height: 3em;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(50, 50, 50, 0.5);
cursor: pointer;
opacity: 1;
transition: opacity 1s;
}

.video-js .vjs-transient-button span {
padding: 0 0.5em;
}

.video-js .vjs-transient-button.left {
left: 5px;
}

.video-js .vjs-transient-button.right {
right: 5px;
}

.video-js .vjs-transient-button.top {
top: 5px;
}

.video-js .vjs-transient-button.bottom {
bottom: 35px;
}

.video-js.vjs-layout-large .vjs-transient-button {
height: 5em;
padding: 0 1em;
font-size: 120%;
}

.video-js.vjs-layout-x-large .vjs-transient-button,
.video-js.vjs-layout-huge .vjs-transient-button {
height: 8em;
padding: 0 1.5em;

font-size: 150%;
}

.video-js .vjs-transient-button.top.avoid-title {
top: 80px;
}

.video-js .vjs-transient-button:hover {
background-color: rgba(50, 50, 50, 0.9);
}

.video-js.not-hover .vjs-transient-button:not(.force-display),
.video-js.vjs-user-inactive .vjs-transient-button:not(.force-display) {
opacity: 0;
}
1 change: 1 addition & 0 deletions src/css/video-js.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
@import "components/captions-settings";
@import "components/title-bar";
@import "components/skip-buttons";
@import "components/transient-button";

@import "print";

Expand Down
77 changes: 77 additions & 0 deletions src/js/transient-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Button from './button.js';
import Component from './component.js';
import {merge} from './utils/obj';
import * as Dom from './utils/dom.js';

// Options type will need to extend button options, because `className`, `clickHandler` may be used
// `position` is redeundant with `className` but allows position to be set without overriding className or vice versa.
const defaults = {
forceTimeout: 4000,
position: 'bottom left'
};

/**
* A floating transient button
*
* @extends Button
*/
class TransientButton extends Button {
/**
*
* @param { import('./player').default } player
*
* @param {object} options
*/
constructor(player, options) {
options = merge(defaults, options);
super(player, options);
this.controlText(options.controlText);
this.hide();

// When shown, the float button will be visible even if the user is inactive.
// Clear this if there is any interaction.
player.on(['useractive', 'userinactive'], () => {
this.removeClass('force-display');
});
}

buildCSSClass() {
return `vjs-transient-button ${this.options_.position}`;
}

createEl() {
/** @type HTMLButtonElement */
const el = Dom.createEl(
'button', {}, {
type: 'button',
class: this.buildCSSClass()
},
Dom.createEl('span')
);

this.controlTextEl_ = el.querySelector('span');

return el;
}

show() {
super.show();
this.addClass('force-display');
this.forceDisplayTimeout = this.player_.setTimeout(() => {
this.removeClass('force-display');
}, this.options_.forceTimeout);
}

hide() {
this.removeClass('force-display');
super.hide();
}

dispose() {
this.player_.clearTimeout(this.forceDisplayTimeout);
super.dispose();
}
}

Component.registerComponent('TransientButton', TransientButton);
export default TransientButton;

0 comments on commit 37e6cc9

Please sign in to comment.