diff options
Diffstat (limited to 'src/js/controls.js')
-rw-r--r-- | src/js/controls.js | 148 |
1 files changed, 68 insertions, 80 deletions
diff --git a/src/js/controls.js b/src/js/controls.js index 57e5bd3f..607511cb 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -9,19 +9,7 @@ import support from './support'; import { repaint, transitionEndEvent } from './utils/animation'; import { dedupe } from './utils/arrays'; import browser from './utils/browser'; -import { - createElement, - emptyElement, - getAttributesFromSelector, - getElement, - getElements, - hasClass, - removeElement, - setAttributes, - toggleClass, - toggleHidden, - matches, -} from './utils/elements'; +import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, toggleClass, toggleHidden } from './utils/elements'; import { off, on } from './utils/events'; import is from './utils/is'; import loadSprite from './utils/loadSprite'; @@ -360,7 +348,7 @@ const controls = { const container = createElement( 'div', extend(attributes, { - class: `plyr__time ${attributes.class}`, + class: `${this.config.classNames.display.time} ${attributes.class ? attributes.class : ''}`.trim(), 'aria-label': i18n.get(type, this.config), }), '00:00', @@ -374,20 +362,43 @@ const controls = { // Create a settings menu item createMenuItem({ value, list, type, title, badge = null, checked = false }) { + const attributes = getAttributesFromSelector(this.config.selectors.inputs[type]); + const item = createElement( 'button', - extend(getAttributesFromSelector(this.config.selectors.inputs[type]), { + extend(attributes, { type: 'button', + role: 'menuitemradio', + class: `${this.config.classNames.control} ${attributes.class ? attributes.class : ''}`.trim(), value, 'aria-checked': checked, - }), - title, + }) ); + // We have to set as HTML incase of special characters + item.innerHTML = title; + if (is.element(badge)) { item.appendChild(badge); } + Object.defineProperty(item, 'checked', { + enumerable: true, + get() { + return item.getAttribute('aria-checked') === 'true'; + }, + set(checked) { + // Ensure exclusivity + if (checked) { + Array.from(item.parentNode.children) + .filter(node => matches(node, '[role="menuitemradio"]')) + .forEach(node => node.setAttribute('aria-checked', 'false')); + } + + item.setAttribute('aria-checked', checked ? 'true' : 'false'); + }, + }); + list.appendChild(item); }, @@ -649,12 +660,12 @@ const controls = { // Set the quality menu setQualityMenu(options) { // Menu required - if (!is.element(this.elements.settings.menus.quality)) { + if (!is.element(this.elements.settings.panels.quality)) { return; } const type = 'quality'; - const list = this.elements.settings.menus.quality.querySelector('[role="menu"]'); + const list = this.elements.settings.panels.quality.querySelector('[role="menu"]'); // Set options if passed and filter based on uniqueness and config if (is.array(options)) { @@ -735,7 +746,7 @@ const controls = { // Update the selected setting updateSetting(setting, container, input) { - const pane = this.elements.settings.menus[setting]; + const pane = this.elements.settings.panels[setting]; let value = null; let list = container; @@ -777,26 +788,26 @@ const controls = { label.innerHTML = controls.getLabel.call(this, setting, value); // Find the radio option and check it - const target = list && list.querySelector(`button[value="${value}"]`); + const target = list && list.querySelector(`[value="${value}"]`); if (is.element(target)) { - target.setAttribute('aria-checked', true); + target.checked = true; } }, // Set the looping options /* setLoopMenu() { // Menu required - if (!is.element(this.elements.settings.menus.loop)) { + if (!is.element(this.elements.settings.panels.loop)) { return; } const options = ['start', 'end', 'all', 'reset']; - const list = this.elements.settings.menus.loop.querySelector('[role="menu"]'); + const list = this.elements.settings.panels.loop.querySelector('[role="menu"]'); // Show the pane and tab toggleHidden(this.elements.settings.buttons.loop, false); - toggleHidden(this.elements.settings.menus.loop, false); + toggleHidden(this.elements.settings.panels.loop, false); // Toggle the pane and tab const toggle = !is.empty(this.loop.options); @@ -835,7 +846,7 @@ const controls = { setCaptionsMenu() { // TODO: Captions or language? Currently it's mixed const type = 'captions'; - const list = this.elements.settings.menus.captions.querySelector('[role="menu"]'); + const list = this.elements.settings.panels.captions.querySelector('[role="menu"]'); const tracks = captions.getTracks.call(this); // Toggle the pane and tab @@ -885,7 +896,7 @@ const controls = { } // Menu required - if (!is.element(this.elements.settings.menus.speed)) { + if (!is.element(this.elements.settings.panels.speed)) { return; } @@ -914,7 +925,7 @@ const controls = { } // Get the list to populate - const list = this.elements.settings.menus.speed.querySelector('[role="menu"]'); + const list = this.elements.settings.panels.speed.querySelector('[role="menu"]'); // Empty the menu emptyElement(list); @@ -986,19 +997,13 @@ const controls = { } }, - // Get the natural size of a tab - getTabSize(tab) { + // Get the natural size of a menu panel + getMenuSize(tab) { const clone = tab.cloneNode(true); clone.style.position = 'absolute'; clone.style.opacity = 0; clone.removeAttribute('hidden'); - // Prevent input's being unchecked due to the name being identical - /* 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); @@ -1015,34 +1020,18 @@ const controls = { }; }, - // Toggle Menu - showMenu(type = '') { - const { menu } = this.elements.settings; - const pane = document.getElementById(`plyr-settings-${this.id}-${type}`); - - console.warn(`plyr-settings-${this.id}-${type}`); + // Show a panel in the menu + showMenuPanel(type = '') { + const target = document.getElementById(`plyr-settings-${this.id}-${type}`); // Nothing to show, bail - if (!is.element(pane)) { - console.warn('No pane found'); + if (!is.element(target)) { return; } - // Are we targeting a tab? If not, bail - /* const isTab = pane.getAttribute('role') === 'tabpanel'; - if (!isTab) { - return; - } */ - - // Hide all other tabs - // Get other tabs - 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 => { - toggle.setAttribute('aria-expanded', false); - }); */ + // Hide all other panels + const container = target.parentNode; + const current = Array.from(container.children).find(node => !node.hidden); // If we can do fancy animations, we'll animate the height/width if (support.transitions && !support.reducedMotion) { @@ -1051,12 +1040,12 @@ const controls = { container.style.height = `${current.scrollHeight}px`; // Get potential sizes - const size = controls.getTabSize.call(this, pane); + const size = controls.getMenuSize.call(this, target); // Restore auto height/width - const restore = e => { + const restore = event => { // We're only bothered about height and width on the container - if (e.target !== container || !['width', 'height'].includes(e.propertyName)) { + if (event.target !== container || !['width', 'height'].includes(event.propertyName)) { return; } @@ -1081,16 +1070,10 @@ const controls = { // current.setAttribute('tabindex', -1); // Set attributes on target - toggleHidden(pane, false); - - /* const tabs = getElements.call(this, `[aria-controls="${target}"]`); - Array.from(tabs).forEach(tab => { - tab.setAttribute('aria-expanded', true); - }); - pane.removeAttribute('tabindex'); */ + toggleHidden(target, false); // Focus the first item - pane.querySelectorAll('button:not(:disabled), input:not(:disabled), [tabindex]')[0].focus(); + target.querySelectorAll('[role^="menuitem"]')[0].focus(); }, // Build the default HTML @@ -1248,12 +1231,12 @@ const controls = { extend(getAttributesFromSelector(this.config.selectors.buttons.settings), { type: 'button', class: `${this.config.classNames.control} ${this.config.classNames.control}--forward`, - 'role': 'menuitem', + role: 'menuitem', 'aria-haspopup': true, }), ); - const flex = createElement('span', null, 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, @@ -1266,7 +1249,6 @@ const controls = { menuItem.appendChild(flex); menu.appendChild(menuItem); - // Build the panes const pane = createElement('div', { id: `plyr-settings-${data.id}-${type}`, @@ -1274,28 +1256,34 @@ const controls = { }); // Back button - pane.appendChild(createElement( + const back = createElement( 'button', { type: 'button', class: `${this.config.classNames.control} ${this.config.classNames.control}--back`, }, i18n.get(type, this.config), - )); + ); + back.addEventListener('click', () => { + controls.showMenuPanel.call(this, 'home'); + }); + pane.appendChild(back); // Menu - pane.appendChild(createElement('div', { - role: 'menu', - })); + pane.appendChild( + createElement('div', { + role: 'menu', + }), + ); inner.appendChild(pane); menuItem.addEventListener('click', () => { - controls.showMenu.call(this, type); + controls.showMenuPanel.call(this, type); }); this.elements.settings.buttons[type] = menuItem; - this.elements.settings.menus[type] = pane; + this.elements.settings.panels[type] = pane; }); home.appendChild(menu); |