const smSelect = document.createElement('template')
smSelect.innerHTML = `
`;
customElements.define('sm-select', class extends HTMLElement {
constructor() {
super()
this.attachShadow({
mode: 'open'
}).append(smSelect.content.cloneNode(true))
this.reset = this.reset.bind(this)
}
static get observedAttributes() {
return ['value']
}
get value() {
return this.getAttribute('value')
}
set value(val) {
this.setAttribute('value', val)
}
reset(){
}
collapse() {
this.chevron.classList.remove('rotate')
this.optionList.animate(this.slideUp, this.animationOptions)
.onfinish = () => {
this.optionList.classList.add('hide')
this.open = false
}
}
connectedCallback() {
this.availableOptions
this.optionList = this.shadowRoot.querySelector('.options')
this.chevron = this.shadowRoot.querySelector('.toggle')
let slot = this.shadowRoot.querySelector('.options slot'),
selection = this.shadowRoot.querySelector('.selection'),
previousOption
this.open = false;
this.slideDown = [{
transform: `translateY(-0.5rem)`,
opacity: 0
},
{
transform: `translateY(0)`,
opacity: 1
}
]
this.slideUp = [{
transform: `translateY(0)`,
opacity: 1
},
{
transform: `translateY(-0.5rem)`,
opacity: 0
}
]
this.animationOptions = {
duration: 300,
fill: "forwards",
easing: 'ease'
}
selection.addEventListener('click', e => {
if (!this.open) {
this.optionList.classList.remove('hide')
this.optionList.animate(this.slideDown, this.animationOptions)
this.chevron.classList.add('rotate')
this.open = true
} else {
this.collapse()
}
})
selection.addEventListener('keydown', e => {
if (e.code === 'ArrowDown' || e.code === 'ArrowRight') {
e.preventDefault()
this.availableOptions[0].focus()
}
if (e.code === 'Enter' || e.code === 'Space')
if (!this.open) {
this.optionList.classList.remove('hide')
this.optionList.animate(this.slideDown, this.animationOptions)
this.chevron.classList.add('rotate')
this.open = true
} else {
this.collapse()
}
})
this.optionList.addEventListener('keydown', e => {
if (e.code === 'ArrowUp' || e.code === 'ArrowRight') {
e.preventDefault()
if (document.activeElement.previousElementSibling) {
document.activeElement.previousElementSibling.focus()
} else {
this.availableOptions[this.availableOptions.length - 1].focus()
}
}
if (e.code === 'ArrowDown' || e.code === 'ArrowLeft') {
e.preventDefault()
if (document.activeElement.nextElementSibling) {
document.activeElement.nextElementSibling.focus()
} else{
this.availableOptions[0].focus()
}
}
})
this.addEventListener('optionSelected', e => {
if (previousOption !== e.target) {
this.setAttribute('value', e.detail.value)
this.shadowRoot.querySelector('.option-text').textContent = e.detail.text;
this.dispatchEvent(new CustomEvent('change', {
bubbles: true,
composed: true,
detail: {
value: e.detail.value
}
}))
if (previousOption) {
previousOption.classList.remove('check-selected')
}
previousOption = e.target;
}
if (!e.detail.switching)
this.collapse()
e.target.classList.add('check-selected')
})
slot.addEventListener('slotchange', e => {
this.availableOptions = slot.assignedElements()
if (this.availableOptions[0]) {
let firstElement = this.availableOptions[0];
previousOption = firstElement;
firstElement.classList.add('check-selected')
this.setAttribute('value', firstElement.getAttribute('value'))
this.shadowRoot.querySelector('.option-text').textContent = firstElement.textContent
this.availableOptions.forEach((element, index) => {
element.setAttribute('data-rank', index + 1);
element.setAttribute('tabindex', "0");
})
}
});
document.addEventListener('mousedown', e => {
if (!this.contains(e.target) && this.open) {
this.collapse()
}
})
}
})
// option
const smOption = document.createElement('template')
smOption.innerHTML = `
`;
customElements.define('sm-option', class extends HTMLElement {
constructor() {
super()
this.attachShadow({
mode: 'open'
}).append(smOption.content.cloneNode(true))
}
sendDetails(switching) {
let optionSelected = new CustomEvent('optionSelected', {
bubbles: true,
composed: true,
detail: {
text: this.textContent,
value: this.getAttribute('value'),
switching: switching
}
})
this.dispatchEvent(optionSelected)
}
connectedCallback() {
let validKey = [
'ArrowUp',
'ArrowDown',
'ArrowLeft',
'ArrowRight'
]
this.addEventListener('click', e => {
this.sendDetails()
})
this.addEventListener('keyup', e => {
if (e.code === 'Enter' || e.code === 'Space') {
e.preventDefault()
this.sendDetails(false)
}
if (validKey.includes(e.code)) {
e.preventDefault()
this.sendDetails(true)
}
})
if (this.hasAttribute('default')) {
setTimeout(() => {
this.sendDetails()
}, 0);
}
}
})