const stripSelect = document.createElement('template'); stripSelect.innerHTML = `
`; customElements.define('strip-select', class extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).append(stripSelect.content.cloneNode(true)); this.stripSelect = this.shadowRoot.querySelector('.strip-select'); this.slottedOptions = undefined; this._value = undefined; this.scrollDistance = 0; this.scrollLeft = this.scrollLeft.bind(this); this.scrollRight = this.scrollRight.bind(this); this.fireEvent = this.fireEvent.bind(this); } get value() { return this._value; } scrollLeft() { this.stripSelect.scrollBy({ left: -this.scrollDistance, behavior: 'smooth' }); } scrollRight() { this.stripSelect.scrollBy({ left: this.scrollDistance, behavior: 'smooth' }); } fireEvent() { this.dispatchEvent( new CustomEvent("change", { bubbles: true, composed: true, detail: { value: this._value } }) ); } connectedCallback() { this.setAttribute('role', 'listbox'); const slot = this.shadowRoot.querySelector('slot'); const coverLeft = this.shadowRoot.querySelector('.cover--left'); const coverRight = this.shadowRoot.querySelector('.cover--right'); const navButtonLeft = this.shadowRoot.querySelector('.nav-button--left'); const navButtonRight = this.shadowRoot.querySelector('.nav-button--right'); slot.addEventListener('slotchange', e => { const assignedElements = slot.assignedElements(); assignedElements.forEach(elem => { if (elem.hasAttribute('selected')) { elem.setAttribute('active', ''); this._value = elem.value; } }); if (!this.hasAttribute('multiline')) { if (assignedElements.length > 0) { firstOptionObserver.observe(slot.assignedElements()[0]); lastOptionObserver.observe(slot.assignedElements()[slot.assignedElements().length - 1]); } else { navButtonLeft.classList.add('hide'); navButtonRight.classList.add('hide'); coverLeft.classList.add('hide'); coverRight.classList.add('hide'); firstOptionObserver.disconnect(); lastOptionObserver.disconnect(); } } }); const resObs = new ResizeObserver(entries => { entries.forEach(entry => { if (entry.contentBoxSize) { // Firefox implements `contentBoxSize` as a single content rect, rather than an array const contentBoxSize = Array.isArray(entry.contentBoxSize) ? entry.contentBoxSize[0] : entry.contentBoxSize; this.scrollDistance = contentBoxSize.inlineSize * 0.6; } else { this.scrollDistance = entry.contentRect.width * 0.6; } }); }); resObs.observe(this); this.stripSelect.addEventListener('option-clicked', e => { if (this._value !== e.target.value) { this._value = e.target.value; slot.assignedElements().forEach(elem => elem.removeAttribute('active')); e.target.setAttribute('active', ''); e.target.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); this.fireEvent(); } }); const firstOptionObserver = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { navButtonLeft.classList.add('hide'); coverLeft.classList.add('hide'); } else { navButtonLeft.classList.remove('hide'); coverLeft.classList.remove('hide'); } }); }, { threshold: 0.9, root: this }); const lastOptionObserver = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { navButtonRight.classList.add('hide'); coverRight.classList.add('hide'); } else { navButtonRight.classList.remove('hide'); coverRight.classList.remove('hide'); } }); }, { threshold: 0.9, root: this }); navButtonLeft.addEventListener('click', this.scrollLeft); navButtonRight.addEventListener('click', this.scrollRight); } disconnectedCallback() { navButtonLeft.removeEventListener('click', this.scrollLeft); navButtonRight.removeEventListener('click', this.scrollRight); } }); //Strip option const stripOption = document.createElement('template'); stripOption.innerHTML = ` `; customElements.define('strip-option', class extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).append(stripOption.content.cloneNode(true)); this._value = undefined; this.radioButton = this.shadowRoot.querySelector('input'); this.fireEvent = this.fireEvent.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); } get value() { return this._value; } fireEvent() { this.dispatchEvent( new CustomEvent("option-clicked", { bubbles: true, composed: true, detail: { value: this._value } }) ); } handleKeyDown(e) { if (e.key === 'Enter' || e.key === 'Space') { this.fireEvent(); } } connectedCallback() { this.setAttribute('role', 'option'); this.setAttribute('tabindex', '0'); this._value = this.getAttribute('value'); this.addEventListener('click', this.fireEvent); this.addEventListener('keydown', this.handleKeyDown); } disconnectedCallback() { this.removeEventListener('click', this.fireEvent); this.removeEventListener('keydown', this.handleKeyDown); } });