aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSam Potts <sam@potts.es>2018-06-18 21:39:47 +1000
committerSam Potts <sam@potts.es>2018-06-18 21:39:47 +1000
commitffd864ed39340c081adb9e4a45b3c9cfe4c139e3 (patch)
tree27eb962a678e9be7bc7e470ec087858df49263b8 /src
parent599883e684cf72a631ea366d0cb821fcb1c3d013 (diff)
downloadplyr-ffd864ed39340c081adb9e4a45b3c9cfe4c139e3.tar.lz
plyr-ffd864ed39340c081adb9e4a45b3c9cfe4c139e3.tar.xz
plyr-ffd864ed39340c081adb9e4a45b3c9cfe4c139e3.zip
Work on controls
Diffstat (limited to 'src')
-rw-r--r--src/js/controls.js198
-rw-r--r--src/js/listeners.js8
-rw-r--r--src/js/plyr.js7
-rw-r--r--src/js/utils/elements.js13
-rw-r--r--src/sass/components/menus.scss24
5 files changed, 119 insertions, 131 deletions
diff --git a/src/js/controls.js b/src/js/controls.js
index d79aaee7..57e5bd3f 100644
--- a/src/js/controls.js
+++ b/src/js/controls.js
@@ -374,34 +374,20 @@ const controls = {
// Create a settings menu item
createMenuItem({ value, list, type, title, badge = null, checked = false }) {
- const item = createElement('li');
-
- const label = createElement('label', {
- class: this.config.classNames.control,
- });
-
- const radio = createElement(
- 'input',
+ const item = createElement(
+ 'button',
extend(getAttributesFromSelector(this.config.selectors.inputs[type]), {
- type: 'radio',
- name: `plyr-${type}`,
+ type: 'button',
value,
- checked,
- class: 'plyr__sr-only',
+ 'aria-checked': checked,
}),
+ title,
);
- const faux = createElement('span', { hidden: '' });
-
- label.appendChild(radio);
- label.appendChild(faux);
- label.insertAdjacentHTML('beforeend', title);
-
if (is.element(badge)) {
- label.appendChild(badge);
+ item.appendChild(badge);
}
- item.appendChild(label);
list.appendChild(item);
},
@@ -656,19 +642,19 @@ const controls = {
},
// Hide/show a tab
- toggleTab(setting, toggle) {
- toggleHidden(this.elements.settings.tabs[setting], !toggle);
+ toggleMenuButton(setting, toggle) {
+ toggleHidden(this.elements.settings.buttons[setting], !toggle);
},
// Set the quality menu
setQualityMenu(options) {
// Menu required
- if (!is.element(this.elements.settings.panes.quality)) {
+ if (!is.element(this.elements.settings.menus.quality)) {
return;
}
const type = 'quality';
- const list = this.elements.settings.panes.quality.querySelector('ul');
+ const list = this.elements.settings.menus.quality.querySelector('[role="menu"]');
// Set options if passed and filter based on uniqueness and config
if (is.array(options)) {
@@ -677,7 +663,7 @@ const controls = {
// Toggle the pane and tab
const toggle = !is.empty(this.options.quality) && this.options.quality.length > 1;
- controls.toggleTab.call(this, type, toggle);
+ controls.toggleMenuButton.call(this, type, toggle);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
@@ -749,7 +735,7 @@ const controls = {
// Update the selected setting
updateSetting(setting, container, input) {
- const pane = this.elements.settings.panes[setting];
+ const pane = this.elements.settings.menus[setting];
let value = null;
let list = container;
@@ -778,7 +764,7 @@ const controls = {
// Get the list if we need to
if (!is.element(list)) {
- list = pane && pane.querySelector('ul');
+ list = pane && pane.querySelector('[role="menu"]');
}
// If there's no list it means it's not been rendered...
@@ -787,34 +773,34 @@ const controls = {
}
// Update the label
- const label = this.elements.settings.tabs[setting].querySelector(`.${this.config.classNames.menu.value}`);
+ const label = this.elements.settings.buttons[setting].querySelector(`.${this.config.classNames.menu.value}`);
label.innerHTML = controls.getLabel.call(this, setting, value);
// Find the radio option and check it
- const target = list && list.querySelector(`input[value="${value}"]`);
+ const target = list && list.querySelector(`button[value="${value}"]`);
if (is.element(target)) {
- target.checked = true;
+ target.setAttribute('aria-checked', true);
}
},
// Set the looping options
/* setLoopMenu() {
// Menu required
- if (!is.element(this.elements.settings.panes.loop)) {
+ if (!is.element(this.elements.settings.menus.loop)) {
return;
}
const options = ['start', 'end', 'all', 'reset'];
- const list = this.elements.settings.panes.loop.querySelector('ul');
+ const list = this.elements.settings.menus.loop.querySelector('[role="menu"]');
// Show the pane and tab
- toggleHidden(this.elements.settings.tabs.loop, false);
- toggleHidden(this.elements.settings.panes.loop, false);
+ toggleHidden(this.elements.settings.buttons.loop, false);
+ toggleHidden(this.elements.settings.menus.loop, false);
// Toggle the pane and tab
const toggle = !is.empty(this.loop.options);
- controls.toggleTab.call(this, 'loop', toggle);
+ controls.toggleMenuButton.call(this, 'loop', toggle);
// Empty the menu
emptyElement(list);
@@ -849,11 +835,11 @@ const controls = {
setCaptionsMenu() {
// TODO: Captions or language? Currently it's mixed
const type = 'captions';
- const list = this.elements.settings.panes.captions.querySelector('ul');
+ const list = this.elements.settings.menus.captions.querySelector('[role="menu"]');
const tracks = captions.getTracks.call(this);
// Toggle the pane and tab
- controls.toggleTab.call(this, type, tracks.length);
+ controls.toggleMenuButton.call(this, type, tracks.length);
// Empty the menu
emptyElement(list);
@@ -899,7 +885,7 @@ const controls = {
}
// Menu required
- if (!is.element(this.elements.settings.panes.speed)) {
+ if (!is.element(this.elements.settings.menus.speed)) {
return;
}
@@ -917,7 +903,7 @@ const controls = {
// Toggle the pane and tab
const toggle = !is.empty(this.options.speed) && this.options.speed.length > 1;
- controls.toggleTab.call(this, type, toggle);
+ controls.toggleMenuButton.call(this, type, toggle);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
@@ -928,7 +914,7 @@ const controls = {
}
// Get the list to populate
- const list = this.elements.settings.panes.speed.querySelector('ul');
+ const list = this.elements.settings.menus.speed.querySelector('[role="menu"]');
// Empty the menu
emptyElement(list);
@@ -948,26 +934,26 @@ const controls = {
// Check if we need to hide/show the settings menu
checkMenu() {
- const { tabs } = this.elements.settings;
- const visible = !is.empty(tabs) && Object.values(tabs).some(tab => !tab.hidden);
+ const { buttons } = this.elements.settings;
+ const visible = !is.empty(buttons) && Object.values(buttons).some(button => !button.hidden);
toggleHidden(this.elements.settings.menu, !visible);
},
// Show/hide menu
toggleMenu(event) {
- const { form } = this.elements.settings;
+ const { popup } = this.elements.settings;
const button = this.elements.buttons.settings;
// Menu and button are required
- if (!is.element(form) || !is.element(button)) {
+ if (!is.element(popup) || !is.element(button)) {
return;
}
- const show = is.boolean(event) ? event : is.element(form) && form.hasAttribute('hidden');
+ const show = is.boolean(event) ? event : is.element(popup) && popup.hasAttribute('hidden');
if (is.event(event)) {
- const isMenuItem = is.element(form) && form.contains(event.target);
+ const isMenuItem = is.element(popup) && popup.contains(event.target);
const isButton = event.target === this.elements.buttons.settings;
// If the click was inside the form or if the click
@@ -988,14 +974,14 @@ const controls = {
button.setAttribute('aria-expanded', show);
}
- if (is.element(form)) {
- toggleHidden(form, !show);
+ if (is.element(popup)) {
+ toggleHidden(popup, !show);
toggleClass(this.elements.container, this.config.classNames.menu.open, show);
if (show) {
- form.removeAttribute('tabindex');
+ popup.removeAttribute('tabindex');
} else {
- form.setAttribute('tabindex', -1);
+ popup.setAttribute('tabindex', -1);
}
}
},
@@ -1008,10 +994,10 @@ const controls = {
clone.removeAttribute('hidden');
// Prevent input's being unchecked due to the name being identical
- Array.from(clone.querySelectorAll('input[name]')).forEach(input => {
+ /* Array.from(clone.querySelectorAll('input[name]')).forEach(input => {
const name = input.getAttribute('name');
input.setAttribute('name', `${name}-clone`);
- });
+ }); */
// Append to parent so we get the "real" size
tab.parentNode.appendChild(clone);
@@ -1030,30 +1016,33 @@ const controls = {
},
// Toggle Menu
- showTab(target = '') {
+ showMenu(type = '') {
const { menu } = this.elements.settings;
- const pane = document.getElementById(target);
+ const pane = document.getElementById(`plyr-settings-${this.id}-${type}`);
+
+ console.warn(`plyr-settings-${this.id}-${type}`);
// Nothing to show, bail
if (!is.element(pane)) {
+ console.warn('No pane found');
return;
}
// Are we targeting a tab? If not, bail
- const isTab = pane.getAttribute('role') === 'tabpanel';
+ /* const isTab = pane.getAttribute('role') === 'tabpanel';
if (!isTab) {
return;
- }
+ } */
// Hide all other tabs
// Get other tabs
- const current = menu.querySelector('[role="tabpanel"]:not([hidden])');
+ const current = menu.querySelector(`[id^=plyr-settings-${this.id}]:not([hidden])`);
const container = current.parentNode;
// Set other toggles to be expanded false
- Array.from(menu.querySelectorAll(`[aria-controls="${current.getAttribute('id')}"]`)).forEach(toggle => {
+ /* Array.from(menu.querySelectorAll(`[aria-controls="${current.getAttribute('id')}"]`)).forEach(toggle => {
toggle.setAttribute('aria-expanded', false);
- });
+ }); */
// If we can do fancy animations, we'll animate the height/width
if (support.transitions && !support.reducedMotion) {
@@ -1089,16 +1078,16 @@ const controls = {
// Set attributes on current tab
toggleHidden(current, true);
- current.setAttribute('tabindex', -1);
+ // current.setAttribute('tabindex', -1);
// Set attributes on target
toggleHidden(pane, false);
- const tabs = getElements.call(this, `[aria-controls="${target}"]`);
+ /* const tabs = getElements.call(this, `[aria-controls="${target}"]`);
Array.from(tabs).forEach(tab => {
tab.setAttribute('aria-expanded', true);
});
- pane.removeAttribute('tabindex');
+ pane.removeAttribute('tabindex'); */
// Focus the first item
pane.querySelectorAll('button:not(:disabled), input:not(:disabled), [tabindex]')[0].focus();
@@ -1220,12 +1209,12 @@ const controls = {
// Settings button / menu
if (this.config.controls.includes('settings') && !is.empty(this.config.settings)) {
- const menu = createElement('div', {
+ const control = createElement('div', {
class: 'plyr__menu',
hidden: '',
});
- menu.appendChild(
+ control.appendChild(
controls.createButton.call(this, 'settings', {
id: `plyr-settings-toggle-${data.id}`,
'aria-haspopup': true,
@@ -1234,48 +1223,38 @@ const controls = {
}),
);
- const form = createElement('form', {
+ const popup = createElement('div', {
class: 'plyr__menu__container',
id: `plyr-settings-${data.id}`,
hidden: '',
'aria-labelled-by': `plyr-settings-toggle-${data.id}`,
- role: 'tablist',
- tabindex: -1,
});
const inner = createElement('div');
const home = createElement('div', {
id: `plyr-settings-${data.id}-home`,
- 'aria-labelled-by': `plyr-settings-toggle-${data.id}`,
- role: 'tabpanel',
});
- // Create the tab list
- const tabs = createElement('ul', {
- role: 'tablist',
+ // Create the menu
+ const menu = createElement('div', {
+ role: 'menu',
});
- // Build the tabs
+ // Build the menu items
this.config.settings.forEach(type => {
- const tab = createElement('li', {
- role: 'tab',
- hidden: '',
- });
-
- const button = createElement(
+ const menuItem = createElement(
'button',
extend(getAttributesFromSelector(this.config.selectors.buttons.settings), {
type: 'button',
class: `${this.config.classNames.control} ${this.config.classNames.control}--forward`,
- id: `plyr-settings-${data.id}-${type}-tab`,
+ 'role': 'menuitem',
'aria-haspopup': true,
- 'aria-controls': `plyr-settings-${data.id}-${type}`,
- 'aria-expanded': false,
}),
- i18n.get(type, this.config),
);
+ const flex = createElement('span', null, i18n.get(type, this.config))
+
const value = createElement('span', {
class: this.config.classNames.menu.value,
});
@@ -1283,54 +1262,51 @@ const controls = {
// Speed contains HTML entities
value.innerHTML = data[type];
- button.appendChild(value);
- tab.appendChild(button);
- tabs.appendChild(tab);
+ flex.appendChild(value);
+ menuItem.appendChild(flex);
+ menu.appendChild(menuItem);
- this.elements.settings.tabs[type] = tab;
- });
- home.appendChild(tabs);
- inner.appendChild(home);
-
- // Build the panes
- this.config.settings.forEach(type => {
+ // Build the panes
const pane = createElement('div', {
id: `plyr-settings-${data.id}-${type}`,
hidden: '',
- 'aria-labelled-by': `plyr-settings-${data.id}-${type}-tab`,
- role: 'tabpanel',
- tabindex: -1,
});
- const back = createElement(
+ // Back button
+ pane.appendChild(createElement(
'button',
{
type: 'button',
class: `${this.config.classNames.control} ${this.config.classNames.control}--back`,
- 'aria-haspopup': true,
- 'aria-controls': `plyr-settings-${data.id}-home`,
- 'aria-expanded': false,
},
i18n.get(type, this.config),
- );
+ ));
- pane.appendChild(back);
+ // Menu
+ pane.appendChild(createElement('div', {
+ role: 'menu',
+ }));
- const options = createElement('ul');
-
- pane.appendChild(options);
inner.appendChild(pane);
- this.elements.settings.panes[type] = pane;
+ menuItem.addEventListener('click', () => {
+ controls.showMenu.call(this, type);
+ });
+
+ this.elements.settings.buttons[type] = menuItem;
+ this.elements.settings.menus[type] = pane;
});
- form.appendChild(inner);
- menu.appendChild(form);
- container.appendChild(menu);
+ home.appendChild(menu);
+ inner.appendChild(home);
+
+ popup.appendChild(inner);
+ control.appendChild(popup);
+ container.appendChild(control);
- this.elements.settings.form = form;
- this.elements.settings.menu = menu;
+ this.elements.settings.popup = popup;
+ this.elements.settings.menu = control;
}
// Picture in picture button
diff --git a/src/js/listeners.js b/src/js/listeners.js
index 9d987508..68a38af2 100644
--- a/src/js/listeners.js
+++ b/src/js/listeners.js
@@ -486,13 +486,12 @@ class Listeners {
});
// Settings menu
- bind(this.player.elements.settings.form, 'click', event => {
+ bind(this.player.elements.settings.popup, 'click', event => {
event.stopPropagation();
// Go back to home tab on click
const showHomeTab = () => {
- const id = `plyr-settings-${this.player.id}-home`;
- controls.showTab.call(this.player, id);
+ controls.showMenu.call(this.player, 'home');
};
// Settings menu items - use event delegation as items are added/removed
@@ -523,9 +522,6 @@ class Listeners {
},
'speed',
);
- } else {
- const tab = event.target;
- controls.showTab.call(this.player, tab.getAttribute('aria-controls'));
}
});
diff --git a/src/js/plyr.js b/src/js/plyr.js
index d4b63874..336a1c38 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -75,16 +75,17 @@ class Plyr {
// Elements cache
this.elements = {
container: null,
+ captions: null,
buttons: {},
display: {},
progress: {},
inputs: {},
settings: {
+ popup: null,
menu: null,
- panes: {},
- tabs: {},
+ menus: {},
+ buttons: {},
},
- captions: null,
};
// Captions
diff --git a/src/js/utils/elements.js b/src/js/utils/elements.js
index 69e4d46c..7b58c9ff 100644
--- a/src/js/utils/elements.js
+++ b/src/js/utils/elements.js
@@ -70,12 +70,19 @@ export function createElement(type, attributes, text) {
// Inaert an element after another
export function insertAfter(element, target) {
+ if (!is.element(element) || !is.element(target)) {
+ return;
+ }
+
target.parentNode.insertBefore(element, target.nextSibling);
}
// Insert a DocumentFragment
export function insertElement(type, parent, attributes, text) {
- // Inject the new <element>
+ if (!is.element(parent)) {
+ return;
+ }
+
parent.appendChild(createElement(type, attributes, text));
}
@@ -95,6 +102,10 @@ export function removeElement(element) {
// Remove all child elements
export function emptyElement(element) {
+ if (!is.element(element)) {
+ return;
+ }
+
let { length } = element.childNodes;
while (length > 0) {
diff --git a/src/sass/components/menus.scss b/src/sass/components/menus.scss
index 3ad4039a..b8dbf5e7 100644
--- a/src/sass/components/menus.scss
+++ b/src/sass/components/menus.scss
@@ -54,18 +54,16 @@
width: 0;
}
- ul {
- list-style: none;
- margin: 0;
- overflow: hidden;
+ [role='menu'] {
padding: $plyr-control-padding;
+ }
- li {
- margin-top: 2px;
+ [role='menuitem'],
+ [role='menuitemradio'] {
+ margin-top: 2px;
- &:first-child {
- margin-top: 0;
- }
+ &:first-child {
+ margin-top: 0;
}
}
@@ -75,10 +73,16 @@
color: $plyr-menu-color;
display: flex;
font-size: $plyr-font-size-menu;
- padding: ceil($plyr-control-padding / 2) ($plyr-control-padding * 2);
+ padding: ceil($plyr-control-padding / 2) ceil($plyr-control-padding * 1.5);
user-select: none;
width: 100%;
+ > span {
+ align-items: inherit;
+ display: flex;
+ width: 100%;
+ }
+
&::after {
border: 4px solid transparent;
content: '';