| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 | import Keyboard from '../modules/keyboard';import DropdownIcon from '../assets/icons/dropdown.svg';let optionsCounter = 0;function toggleAriaAttribute(element, attribute) {  element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true'));}class Picker {  constructor(select) {    this.select = select;    this.container = document.createElement('span');    this.buildPicker();    this.select.style.display = 'none';    this.select.parentNode.insertBefore(this.container, this.select);    this.label.addEventListener('mousedown', () => {      this.togglePicker();    });    this.label.addEventListener('keydown', (event) => {      switch(event.keyCode) {        // Allows the "Enter" key to open the picker        case Keyboard.keys.ENTER:          this.togglePicker();          break;        // Allows the "Escape" key to close the picker        case Keyboard.keys.ESCAPE:          this.escape();          event.preventDefault();          break;        default:      }    });    this.select.addEventListener('change', this.update.bind(this));  }  togglePicker() {    this.container.classList.toggle('ql-expanded');    // Toggle aria-expanded and aria-hidden to make the picker accessible    toggleAriaAttribute(this.label, 'aria-expanded');    toggleAriaAttribute(this.options, 'aria-hidden');  }  buildItem(option) {    let item = document.createElement('span');    item.tabIndex = '0';    item.setAttribute('role', 'button');    item.classList.add('ql-picker-item');    if (option.hasAttribute('value')) {      item.setAttribute('data-value', option.getAttribute('value'));    }    if (option.textContent) {      item.setAttribute('data-label', option.textContent);    }    item.addEventListener('click', () => {      this.selectItem(item, true);    });    item.addEventListener('keydown', (event) => {      switch(event.keyCode) {        // Allows the "Enter" key to select an item        case Keyboard.keys.ENTER:          this.selectItem(item, true);          event.preventDefault();          break;        // Allows the "Escape" key to close the picker        case Keyboard.keys.ESCAPE:          this.escape();          event.preventDefault();          break;        default:      }    });    return item;  }  buildLabel() {    let label = document.createElement('span');    label.classList.add('ql-picker-label');    label.innerHTML = DropdownIcon;    label.tabIndex = '0';    label.setAttribute('role', 'button');    label.setAttribute('aria-expanded', 'false');    this.container.appendChild(label);    return label;  }  buildOptions() {    let options = document.createElement('span');    options.classList.add('ql-picker-options');    // Don't want screen readers to read this until options are visible    options.setAttribute('aria-hidden', 'true');    options.tabIndex = '-1';    // Need a unique id for aria-controls    options.id = `ql-picker-options-${optionsCounter}`;    optionsCounter += 1;    this.label.setAttribute('aria-controls', options.id);    this.options = options;    [].slice.call(this.select.options).forEach((option) => {      let item = this.buildItem(option);      options.appendChild(item);      if (option.selected === true) {        this.selectItem(item);      }    });    this.container.appendChild(options);  }  buildPicker() {    [].slice.call(this.select.attributes).forEach((item) => {      this.container.setAttribute(item.name, item.value);    });    this.container.classList.add('ql-picker');    this.label = this.buildLabel();    this.buildOptions();  }  escape() {    // Close menu and return focus to trigger label    this.close();    // Need setTimeout for accessibility to ensure that the browser executes    // focus on the next process thread and after any DOM content changes    setTimeout(() => this.label.focus(), 1);  }  close() {    this.container.classList.remove('ql-expanded');    this.label.setAttribute('aria-expanded', 'false');    this.options.setAttribute('aria-hidden', 'true');  }  selectItem(item, trigger = false) {    let selected = this.container.querySelector('.ql-selected');    if (item === selected) return;    if (selected != null) {      selected.classList.remove('ql-selected');    }    if (item == null) return;    item.classList.add('ql-selected');    this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item);    if (item.hasAttribute('data-value')) {      this.label.setAttribute('data-value', item.getAttribute('data-value'));    } else {      this.label.removeAttribute('data-value');    }    if (item.hasAttribute('data-label')) {      this.label.setAttribute('data-label', item.getAttribute('data-label'));    } else {      this.label.removeAttribute('data-label');    }    if (trigger) {      if (typeof Event === 'function') {        this.select.dispatchEvent(new Event('change'));      } else if (typeof Event === 'object') {     // IE11        let event = document.createEvent('Event');        event.initEvent('change', true, true);        this.select.dispatchEvent(event);      }      this.close();    }  }  update() {    let option;    if (this.select.selectedIndex > -1) {      let item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex];      option = this.select.options[this.select.selectedIndex];      this.selectItem(item);    } else {      this.selectItem(null);    }    let isActive = option != null && option !== this.select.querySelector('option[selected]');    this.label.classList.toggle('ql-active', isActive);  }}export default Picker;
 |