import supportDom from '../decorators/supportDom' import Dropdown from './Dropdown' import { noop } from '../utils'
@supportDom export default class Tabbox {
constructor(dom, options = {}) { this.currentNode = null this.optionEl = null this.dom = dom this.options = options this.options.change = options.change || noop this.options.click = options.click || noop this.init() } init() { const { dom } = this this.btns = Array.from(dom.querySelectorAll('button[data-tabbox-item]')) this.dropdownBtns = Array.from(dom.querySelectorAll('button[data-tabbox-dropdown]')) this.addEvents() this.appendSlider() } adjustSlider() { const [firstBtn] = this.btns if (! firstBtn) { return } const { slider } = this slider.style.top = (firstBtn.offsetHeight - slider.offsetHeight) + 'px' slider.style.left = firstBtn.offsetLeft + 'px' } moveTo(name) { const target = this.dom.querySelector(`[data-tabbox-item=${name}]`) if (target) { this.removeCurrentClass() this.currentNode = target this.moveToCurrentNode() this.addCurrentClass() } } eachDropdownOption(fn) { let index = 0 for (const d of this.dropdownInstances) { const options = Array.from(d.menu.querySelectorAll('[data-tabbox-item]')) for (const optionEl of options) { const going = fn({ dropdownBtn: this.dropdownBtns[index], dropdownInstance: d, optionEl }) if (going === false) { return } } ++index } } getDefaultDropdownData() { let res = {} this.eachDropdownOption(({ dropdownBtn, dropdownInstance, optionEl }) => { if ('default' in optionEl.dataset) { res = { defaultDropdownBtn: dropdownBtn, defaultDropdownInstance: dropdownInstance, defaultOptionEl: optionEl } return false } }) return res } appendSlider() { this.slider = document.createElement('div') this.slider.classList.add('js-slider') this.dom.appendChild(this.slider) this.defaultSliderColor = getComputedStyle(this.slider).getPropertyValue('background-color') const defaultBtn = this.btns.find(btn => 'default' in btn.dataset) this.adjustSlider() if (defaultBtn) { this.currentNode = defaultBtn || defaultSelectBox this.moveToCurrentNode() this.addCurrentClass() } const { defaultDropdownBtn, defaultDropdownInstance, defaultOptionEl } = this.getDefaultDropdownData() if (defaultDropdownBtn) { this.currentNode = defaultDropdownBtn this.optionEl = defaultOptionEl this.moveToCurrentNode() this.addCurrentClass() defaultDropdownInstance.setText(this.optionEl.textContent) } } setSliderColor(color) { this.slider.style.backgroundColor = color } moveToCurrentNode() { const node = this.currentNode if (! node) { return } if ('tabboxDropdown' in node.dataset) { return this.moveSlider({ top: node.offsetTop, left: node.offsetLeft, width: node.offsetWidth, color: this.optionEl.dataset.activeColor }) } this.moveSlider({ top: node.offsetTop, left: node.offsetLeft, width: node.offsetWidth, color: node.dataset.activeColor }) } moveSlider({ top, left, width, color }) { this.slider.style.transform = `translate(${left}px, ${top}px)` this.slider.style.width = width + 'px' const defaultSliderColor = this.defaultSliderColor || '#858585' this.slider.style.backgroundColor = color || defaultSliderColor } removeCurrentClass() { if (this.currentNode) { this.currentNode.classList.remove('js-current') } } addCurrentClass() { if (this.currentNode) { this.currentNode.classList.add('js-current') } } setStatus(status) { const btn = this.btns.find(btn => btn.dataset.tabboxItem === status) if (btn) { this.removeCurrentClass() this.currentNode = btn this.moveToCurrentNode() this.addCurrentClass() this.options.change({ id: status, type: 'btn' }) return } let dropdownMatched = false this.eachDropdownOption(({ dropdownBtn, dropdownInstance, optionEl }) => { if (status === optionEl.dataset.tabboxItem) { this.setDropdown({ dropdownBtn, dropdownInstance, optionEl }) this.options.change({ id: status, type: 'dropdown' }) dropdownMatched = true return false } }) if (! dropdownMatched) { throw new Error(`Cannot find status: ${status}`) } } setDropdown({ dropdownBtn, dropdownInstance, optionEl }) { this.removeCurrentClass() this.currentNode = dropdownBtn this.optionEl = optionEl this.moveToCurrentNode() this.addCurrentClass() this.dropdownInstances.filter(d => d !== dropdownInstance) .forEach(d => d.restoreText()) dropdownInstance.setText(this.optionEl.textContent) } addEvents() { this.dropdownInstances = this.dropdownBtns.map(el => { const dropdownInstance = new Dropdown(el, { menuClick: event => { const id = event.target.dataset.tabboxItem this.options.click({ id, type: 'dropdown' }) if (this.optionEl === event.target) { return } this.setDropdown({ dropdownBtn: el, optionEl: event.target, dropdownInstance }) this.options.change({ id, type: 'dropdown' }) } }) return dropdownInstance }) this.btns.forEach(btn => { this.addEvent(btn, 'click', () => { const id = btn.dataset.tabboxItem this.options.click({ id, type: 'btn' }) if (btn !== this.currentNode) { this.dropdownInstances.forEach(d => d.restoreText()) this.removeCurrentClass() this.currentNode = btn this.optionEl = null this.moveToCurrentNode() this.addCurrentClass() this.options.change({ id, type: 'btn' }) } }) }) } destroy() { this.currentNode = null this.dropdownInstances.forEach(d => d.destroy()) this.slider.parentNode.removeChild(this.slider) }
}