UNPKG

roseworx

Version:

Front end css and js framework

225 lines (202 loc) 6.08 kB
import { rwxCore, rwxComponent } from '../rwxCore'; import rwxDOM from '../helpers/rwxDOMHelpers'; import rwxMist from '../common/rwxMist'; import {rwxAnimationChain} from '../modules/rwxAnimation'; class rwxTabs extends rwxCore { constructor() { super({selector:'[rwx-tabs]', canHaveManualControl:false, resource: 'rwxTabs'}); } execute(el) { return new rwxTab(el); } goToTab(id, tabNumber) { if(!this.validateParameter(tabNumber, 'number', 'goToTab'))return; const IME = this.getIME(id); IME && IME.changeTab(tabNumber); } } class rwxTab extends rwxComponent { constructor(el) { super({element: el, enableCustomEvents: true}); this.tabs = [...el.children].filter((c)=>c.classList.contains('rwx-tabs-tab')); if(this.tabs.length == 0){return;} this.change = this.change.bind(this); this.tabHeaders = []; let initTab = this.tabs.findIndex((t)=>t.hasAttribute('data-rwx-tabs-initial')); this.activeTab = initTab === -1 ? 1 : initTab+1; this.declareEvent('tabShow'); this.declareEvent('tabHide'); this.autoActiveTabFromLocationHash(); this.createTabs(); this.createAnimations(); } createAnimations() { this.animation = new rwxAnimationChain({ sequence: [ { from: [1, 1], to: [0, 0.5], easing:['easeInCubic', 'linear'], duration: 300, complete: ()=>this.tabHidden() }, { to: [1, 1], easing: ['easeOutCubic', 'linear'], duration:300, complete: ()=>this.tabShown() } ], complete: ()=>{ this.stopAnimation =true; this.animating=false; } }) } autoActiveTabFromLocationHash() { if(window.location.hash) { const validSelector = selector => { try { document.createDocumentFragment().querySelector(selector) } catch(e) { return false } return true } const selector = `#${window.location.hash.replace('#', '')}`; if(!validSelector(selector))return; let el = this.el.querySelector(selector); if(el) { let tab = rwxDOM.hasAncestor(el, '.rwx-tabs-tab'); let parent = tab ? rwxDOM.hasAncestor(tab, '.rwx-tabs-tab') : false; if(parent && this.el.contains(parent)){this.compareAndOpen(parent); return;} if(tab){this.compareAndOpen(tab); this.hash = el;} } } } compareAndOpen(compare) { this.tabs.map((t,i)=>{ if(t == compare) { this.activeTab = (i+1); } }) } cleanUp() { this.el.removeChild(this.container); this.tabs.map((t)=>{ t.classList.remove('initial-hide'); t.style.transform = ""; t.style.opacity = ""; t.style.display = ""; return false; }); } createTabs() { this.container = document.createElement('div'); this.container.classList.add('rwx-tabs-container'); this.bullet = document.createElement('span'); this.bullet.classList.add('bullet'); this.addElement(this.container, this.bullet); let buttonContainer = document.createElement('div'); buttonContainer.classList.add('rwx-tabs-button-container'); this.tabs.map((t, i)=>{ if(t.hasAttribute('data-rwx-tabs-title')) { let button = document.createElement('button'); let text = document.createTextNode(t.getAttribute('data-rwx-tabs-title')); button.appendChild(text); button.classList.add('no-decoration'); button.setAttribute('role', 'tab'); button.setAttribute('aria-selected', 'false'); button.addEventListener('click', ()=>{ this.changeTab(i+1); }); button.addEventListener('keydown', (e)=>{ if(e.keyCode == 39) { this.tabHeaders[i+1 == this.tabs.length ? 0 : i+1].focus(); } else if (e.keyCode == 37) { this.tabHeaders[i == 0 ? this.tabs.length-1 : i-1].focus(); } }); this.tabHeaders.push(button); buttonContainer.appendChild(button); } if(i+1 == this.activeTab){ this.tabHeaders[i].setAttribute('aria-selected', true); this.tabHeaders[i].classList.add('active'); window.requestAnimationFrame(()=>{this.moveBullet(this.activeTab)}); } else{t.classList.add('initial-hide')} return; }); this.container.appendChild(buttonContainer); this.el.insertBefore(this.container, this.tabs[0]); new rwxMist(this.container); if(this.hash) { window.requestAnimationFrame(()=>{window.scrollTo(0, window.scrollY + this.hash.getBoundingClientRect().top-40);}); } } resetAnimationFlags() { this.stopAnimation = false; this.animation.reset(); } changeTab(tabNumber) { if(tabNumber == this.activeTab || this.animating || tabNumber < 0 || tabNumber > this.tabs.length){return;} this.newTabNumber = tabNumber; this.resetAnimationFlags(); this.moveBullet(tabNumber); this.animating = true; this.change(); this.tabHeaders.map((th,i)=>(tabNumber-1 == i) ? th.classList.add('active') : th.classList.remove('active')) } moveBullet(tabNumber) { let rect = this.tabHeaders[tabNumber-1].getBoundingClientRect(); let left = (rect.left - this.el.getBoundingClientRect().left) + this.container.scrollLeft; this.bullet.style.left = `${left}px`; this.bullet.style.width = `${rect.width}px`; } change() { this.animation.animate([ (s, o)=>{ this.tabs[this.activeTab-1].style.transform = `scale(${s})`; this.tabs[this.activeTab-1].style.opacity = o; }, (s, o)=>{ this.tabs[this.activeTab-1].style.transform = `scale(${s})`; this.tabs[this.activeTab-1].style.opacity = o; } ]); if(this.stopAnimation){return;} window.requestAnimationFrame(this.change); } tabShown() { this.animating = false; this.tabHeaders[this.activeTab-1].setAttribute('aria-selected', 'true'); this.executeEvent('tabShow', this.activeTab); } tabHidden() { let cache = this.activeTab; this.tabHeaders[this.activeTab-1].setAttribute('aria-selected', 'false'); this.tabs[this.activeTab-1].style.display = "none"; this.activeTab = this.newTabNumber; this.tabs[this.activeTab-1].classList.remove('initial-hide'); this.tabs[this.activeTab-1].style.display = "none"; this.tabs[this.activeTab-1].removeAttribute('style'); this.executeEvent('tabHide', cache); } } export default new rwxTabs();