diff options
| author | Sam Potts <sam@potts.es> | 2018-06-18 21:39:47 +1000 | 
|---|---|---|
| committer | Sam Potts <sam@potts.es> | 2018-06-18 21:39:47 +1000 | 
| commit | ffd864ed39340c081adb9e4a45b3c9cfe4c139e3 (patch) | |
| tree | 27eb962a678e9be7bc7e470ec087858df49263b8 /src | |
| parent | 599883e684cf72a631ea366d0cb821fcb1c3d013 (diff) | |
| download | plyr-ffd864ed39340c081adb9e4a45b3c9cfe4c139e3.tar.lz plyr-ffd864ed39340c081adb9e4a45b3c9cfe4c139e3.tar.xz plyr-ffd864ed39340c081adb9e4a45b3c9cfe4c139e3.zip | |
Work on controls
Diffstat (limited to 'src')
| -rw-r--r-- | src/js/controls.js | 198 | ||||
| -rw-r--r-- | src/js/listeners.js | 8 | ||||
| -rw-r--r-- | src/js/plyr.js | 7 | ||||
| -rw-r--r-- | src/js/utils/elements.js | 13 | ||||
| -rw-r--r-- | src/sass/components/menus.scss | 24 | 
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: ''; | 
