Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
483 changes: 1 addition & 482 deletions docs/subtitles-and-captions.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
{
"path": "./lib/all.js",
"maxSize": "320kb"
"maxSize": "325kb"
}
]
},
Expand Down
132 changes: 132 additions & 0 deletions src/components/dynamicTextTracksButton/dynamic-text-tracks-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/* eslint-disable */
import videojs from 'video.js';
import './dynamic-text-tracks-button.scss';

const Component = videojs.getComponent('Component');

class SearchableLanguageDropdown extends Component {
constructor(player, options = {}) {
super(player, options);

this.player = player;
this.languages = options.languages || [];
this.onSelect = options.onSelect || (() => {});
this.open = false;
this.dropdownEl = null;

this.el().addEventListener('click', (e) => this.handleToggle(e));
}

createEl() {
return videojs.dom.createEl('div', {
className: 'vjs-searchable-language-dropdown vjs-control vjs-button vjs-icon-placeholder',
innerHTML: `
<button class="vjs-lang-toggle" aria-label="Select Language" title="Select Language"></button>
`
});
}

handleToggle(e) {
const isToggle = e.target.classList.contains('vjs-lang-toggle');
if (isToggle) {
this.open ? this.hideDropdown() : this.showDropdown();
e.stopPropagation();
}
}

createDropdown() {
this.dropdownEl = document.createElement('div');
this.dropdownEl.className = 'vjs-lang-popover';
this.dropdownEl.innerHTML = `
<input type="text" placeholder="Search..." class="vjs-lang-search">
<div class="vjs-lang-header">Languages</div>
<ul class="vjs-lang-list"></ul>
`;
const container = this.player.el_ || document.body;
container.appendChild(this.dropdownEl);
}

showDropdown() {
if (!this.dropdownEl) this.createDropdown();

this.open = true;
this.dropdownEl.style.position = 'absolute';
this.dropdownEl.style.right = 0;
this.dropdownEl.style.bottom = '43px';
this.dropdownEl.style.zIndex = 9999;
this.dropdownEl.style.display = 'block';

const input = this.dropdownEl.querySelector('.vjs-lang-search');
input.value = '';
input.focus();
input.addEventListener('input', () => {
this.renderList(input.value.toLowerCase());
});

this.renderList('');
}

hideDropdown() {
this.open = false;
if (this.dropdownEl) {
this.dropdownEl.style.display = 'none';
}
}

renderList(query = '') {
const ul = this.dropdownEl.querySelector('.vjs-lang-list');
ul.innerHTML = '';

const filtered = this.languages.filter(l =>
l.label.toLowerCase().includes(query)
);

if (filtered.length === 0) {
const li = document.createElement('li');
li.className = 'vjs-lang-empty';
li.textContent = 'No results...';
ul.appendChild(li);
return;
}

filtered.forEach(lang => {
const status = lang.status || 'idle';
const icon = {
idle: '',
loading: '⏳',
loaded: '✅',
error: '❌'
}[status] || '';

const li = document.createElement('li');
li.className = `vjs-lang-item vjs-lang-${status}`;
li.innerHTML = `
<span>${lang.label}</span>
<span class="vjs-lang-icon">${icon}</span>
`;

if (lang.selected) {
li.classList.add('vjs-lang-selected');
}

li.addEventListener('click', (e) => {
e.stopPropagation();
this.onSelect(lang);
// Keep dropdown open
});

ul.appendChild(li);
});
}

updateLanguages(languages) {
this.languages = languages;
if (this.open) {
const q = this.dropdownEl.querySelector('.vjs-lang-search').value.toLowerCase();
this.renderList(q);
}
}
}

videojs.registerComponent('SearchableLanguageDropdown', SearchableLanguageDropdown);
export default SearchableLanguageDropdown;
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
.cld-video-player {
position: relative;
}

.vjs-searchable-language-dropdown.vjs-control.vjs-button {
font-size: 1.8em;
cursor: pointer;
position: relative;
padding: 0;
color: inherit;
width: 36px;

.vjs-lang-toggle {
all: unset;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: inherit;
line-height: 1;
cursor: pointer;
user-select: none;
position: relative;
z-index: 9999;
}

&:hover,
&:focus-within {
background-color: rgba(255, 255, 255, 0.2);
}

&.vjs-icon-placeholder button::after {
font-family: "VideoJS";
content: "\f10b";
font-style: normal;
font-weight: normal;
text-transform: none;
speak: none;
line-height: 1;
}
}

.vjs-lang-popover {
position: absolute;
background: #1c1c1c;
border: 1px solid #444;
color: white;
width: 220px;
padding: 10px;
z-index: 10000;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
font-family: sans-serif;
font-size: 13px;
}

.vjs-lang-search {
width: 100%;
padding: 6px 8px;
margin-bottom: 8px;
border: 1px solid #555;
border-radius: 3px;
background: #111;
color: white;
font-size: 13px;
}

.vjs-lang-header {
font-weight: bold;
color: #ccc;
font-size: 12px;
margin-bottom: 8px;
border-bottom: 1px solid #333;
padding-bottom: 4px;
}

.vjs-lang-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 160px;
overflow-y: auto;
}

.vjs-lang-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
cursor: pointer;
border-radius: 3px;
transition: background 0.2s;
color: white;
}

.vjs-lang-item:hover {
background: #333;
}

.vjs-lang-item.vjs-lang-loaded {
opacity: 0.5;
}

.vjs-lang-item.vjs-lang-error {
color: #f66;
}

.vjs-lang-item.vjs-lang-loading {
color: #ffc107;
}

.vjs-lang-selected {
background: #6c8bd5 !important;
font-weight: bold;
}

.vjs-lang-icon {
padding-left: 8px;
}

.vjs-lang-empty {
padding: 6px 8px;
font-style: italic;
color: #888;
}
4 changes: 3 additions & 1 deletion src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import JumpBackButton from './jumpButtons/jump-10-minus';
import LogoButton from './logoButton/logo-button';
import ProgressControlEventsBlocker from './progress-control-events-blocker/progress-control-events-blocker';
import TitleBar from './title-bar/title-bar';
import DynamicTextTracksButton from './dynamicTextTracksButton/dynamic-text-tracks-button';

export {
JumpForwardButton,
JumpBackButton,
LogoButton,
ProgressControlEventsBlocker,
TitleBar
TitleBar,
DynamicTextTracksButton
};
1 change: 1 addition & 0 deletions src/index.all.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import cloudinary from './index.js';
export * from './index.js';
export * from './plugins/adaptive-streaming/adaptive-streaming.js';
export * from './plugins/chapters/chapters.js';
export * from './plugins/dynamic-text-tracks/index.js';
export * from './plugins/colors/colors.js';
export * from './plugins/ima/ima.js';
export * from './plugins/playlist/playlist.js';
Expand Down
102 changes: 102 additions & 0 deletions src/plugins/dynamic-text-tracks/dynamic-text-tracks.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
.vjs-subs-caps-button {
display: none !important;
}


.vjs-searchable-language-dropdown {
position: relative;
display: flex;
align-items: center;

.vjs-lang-toggle {
background: transparent;
border: none;
color: white;
font-size: 16px;
width: 100%;
height: 100%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}

.vjs-lang-popover {
position: absolute;
bottom: 100%;
left: 0;
background: #1c1c1c;
border: 1px solid #333;
padding: 8px;
width: 200px;
z-index: 9999;
border-radius: 4px;
margin-bottom: 8px;
}

.hidden {
display: none;
}

.vjs-lang-search {
width: 100%;
padding: 5px;
margin-bottom: 6px;
border: 1px solid #444;
background: #111;
color: white;
border-radius: 3px;
}

.vjs-lang-header {
font-size: 12px;
color: #aaa;
font-weight: bold;
margin-bottom: 6px;
border-bottom: 1px solid #333;
padding-bottom: 2px;
}

.vjs-lang-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 160px;
overflow-y: auto;

.vjs-lang-item {
padding: 6px 5px;
display: flex;
justify-content: space-between;
cursor: pointer;
color: white;

&:hover {
background: #333;
}

&.vjs-lang-loaded {
opacity: 0.6;
}

&.vjs-lang-error {
color: #f44;
}

&.vjs-lang-loading {
color: #ffcc00;
}

.vjs-lang-icon {
padding-left: 6px;
}
}

.vjs-lang-empty {
padding: 6px;
color: #888;
font-style: italic;
}
}
}
Loading
Loading