Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adds a transient button component #8629

Merged
merged 13 commits into from
Jul 6, 2024
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>
62 changes: 62 additions & 0 deletions src/css/components/_transient-button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.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:not(.vjs-has-started) .vjs-transient-button {
display: none;
}

.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;
}
mister-ben marked this conversation as resolved.
Show resolved Hide resolved

.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%;
}
mister-ben marked this conversation as resolved.
Show resolved Hide resolved

.video-js .vjs-transient-button.top.avoid-title {
top: 80px;
}
mister-ben marked this conversation as resolved.
Show resolved Hide resolved

.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
1 change: 1 addition & 0 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import './tracks/text-track-settings.js';
import './resize-manager.js';
import './live-tracker.js';
import './title-bar.js';
import './transient-button.js';

// Import Html5 tech, at least for disposing the original video tag.
import './tech/html5.js';
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();

Check warning on line 29 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L25-L29

Added lines #L25 - L29 were not covered by tests

// 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');

Check warning on line 34 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L33-L34

Added lines #L33 - L34 were not covered by tests
});
}

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

Check warning on line 39 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L38-L39

Added lines #L38 - L39 were not covered by tests
mister-ben marked this conversation as resolved.
Show resolved Hide resolved
}

createEl() {

Check warning on line 42 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L42

Added line #L42 was not covered by tests
/** @type HTMLButtonElement */
const el = Dom.createEl(

Check warning on line 44 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L44

Added line #L44 was not covered by tests
'button', {}, {
type: 'button',
class: this.buildCSSClass()
},
Dom.createEl('span')
);

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

Check warning on line 52 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L52

Added line #L52 was not covered by tests

return el;

Check warning on line 54 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L54

Added line #L54 was not covered by tests
}

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

Check warning on line 61 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L57-L61

Added lines #L57 - L61 were not covered by tests
}, this.options_.forceTimeout);
}

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

Check warning on line 67 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L65-L67

Added lines #L65 - L67 were not covered by tests
}

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

Check warning on line 72 in src/js/transient-button.js

View check run for this annotation

Codecov / codecov/patch

src/js/transient-button.js#L70-L72

Added lines #L70 - L72 were not covered by tests
}
}

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