You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.6 KiB
171 lines
5.6 KiB
const DropdownIcon = "<svg viewbox=\"0 0 18 18\"><polygon class=\"ql-stroke\" points=\"7 11 9 13 11 11 7 11\"/><polygon class=\"ql-stroke\" points=\"7 7 9 5 11 7 7 7\"/></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'; |
|
// @ts-expect-error Fix me later |
|
this.select.parentNode.insertBefore(this.container, this.select); |
|
this.label.addEventListener('mousedown', () => { |
|
this.togglePicker(); |
|
}); |
|
this.label.addEventListener('keydown', event => { |
|
switch (event.key) { |
|
case 'Enter': |
|
this.togglePicker(); |
|
break; |
|
case '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'); |
|
// @ts-expect-error |
|
toggleAriaAttribute(this.options, 'aria-hidden'); |
|
} |
|
buildItem(option) { |
|
const item = document.createElement('span'); |
|
// @ts-expect-error |
|
item.tabIndex = '0'; |
|
item.setAttribute('role', 'button'); |
|
item.classList.add('ql-picker-item'); |
|
const value = option.getAttribute('value'); |
|
if (value) { |
|
item.setAttribute('data-value', value); |
|
} |
|
if (option.textContent) { |
|
item.setAttribute('data-label', option.textContent); |
|
} |
|
item.addEventListener('click', () => { |
|
this.selectItem(item, true); |
|
}); |
|
item.addEventListener('keydown', event => { |
|
switch (event.key) { |
|
case 'Enter': |
|
this.selectItem(item, true); |
|
event.preventDefault(); |
|
break; |
|
case 'Escape': |
|
this.escape(); |
|
event.preventDefault(); |
|
break; |
|
default: |
|
} |
|
}); |
|
return item; |
|
} |
|
buildLabel() { |
|
const label = document.createElement('span'); |
|
label.classList.add('ql-picker-label'); |
|
label.innerHTML = DropdownIcon; |
|
// @ts-expect-error |
|
label.tabIndex = '0'; |
|
label.setAttribute('role', 'button'); |
|
label.setAttribute('aria-expanded', 'false'); |
|
this.container.appendChild(label); |
|
return label; |
|
} |
|
buildOptions() { |
|
const 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'); |
|
// @ts-expect-error |
|
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); |
|
|
|
// @ts-expect-error |
|
this.options = options; |
|
Array.from(this.select.options).forEach(option => { |
|
const item = this.buildItem(option); |
|
options.appendChild(item); |
|
if (option.selected === true) { |
|
this.selectItem(item); |
|
} |
|
}); |
|
this.container.appendChild(options); |
|
} |
|
buildPicker() { |
|
Array.from(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'); |
|
// @ts-expect-error |
|
this.options.setAttribute('aria-hidden', 'true'); |
|
} |
|
selectItem(item) { |
|
let trigger = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; |
|
const 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'); |
|
// @ts-expect-error Fix me later |
|
this.select.selectedIndex = Array.from(item.parentNode.children).indexOf(item); |
|
if (item.hasAttribute('data-value')) { |
|
// @ts-expect-error Fix me later |
|
this.label.setAttribute('data-value', item.getAttribute('data-value')); |
|
} else { |
|
this.label.removeAttribute('data-value'); |
|
} |
|
if (item.hasAttribute('data-label')) { |
|
// @ts-expect-error Fix me later |
|
this.label.setAttribute('data-label', item.getAttribute('data-label')); |
|
} else { |
|
this.label.removeAttribute('data-label'); |
|
} |
|
if (trigger) { |
|
this.select.dispatchEvent(new Event('change')); |
|
this.close(); |
|
} |
|
} |
|
update() { |
|
let option; |
|
if (this.select.selectedIndex > -1) { |
|
const item = |
|
// @ts-expect-error Fix me later |
|
this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex]; |
|
option = this.select.options[this.select.selectedIndex]; |
|
// @ts-expect-error |
|
this.selectItem(item); |
|
} else { |
|
this.selectItem(null); |
|
} |
|
const isActive = option != null && option !== this.select.querySelector('option[selected]'); |
|
this.label.classList.toggle('ql-active', isActive); |
|
} |
|
} |
|
export default Picker; |
|
//# sourceMappingURL=picker.js.map
|