jellyfish-web/src/elements/emby-tabs/emby-tabs.js

324 lines
9.4 KiB
JavaScript
Raw Normal View History

2020-08-14 08:46:34 +02:00
import dom from '../../scripts/dom';
import scroller from '../../libraries/scroller';
import browser from '../../scripts/browser';
import focusManager from '../../components/focusManager';
2020-09-08 02:43:56 -04:00
import 'webcomponents.js/webcomponents-lite';
2020-08-14 08:46:34 +02:00
import './emby-tabs.css';
import '../../assets/css/scrollstyles.css';
/* eslint-disable indent */
2020-07-11 11:23:28 +01:00
const buttonClass = 'emby-tab-button';
const activeButtonClass = buttonClass + '-active';
2018-10-23 01:05:09 +03:00
2020-08-18 20:31:10 +02:00
function setActiveTabButton(newButton) {
newButton.classList.add(activeButtonClass);
2018-10-23 01:05:09 +03:00
}
function getTabPanel(tabs, index) {
return null;
2018-10-23 01:05:09 +03:00
}
function removeActivePanelClass(tabs, index) {
2020-07-17 10:33:31 +02:00
const tabPanel = getTabPanel(tabs, index);
if (tabPanel) {
tabPanel.classList.remove('is-active');
}
}
2018-10-23 01:05:09 +03:00
function fadeInRight(elem) {
2020-07-11 11:23:28 +01:00
const pct = browser.mobile ? '4%' : '0.5%';
2020-07-11 11:23:28 +01:00
const keyframes = [
{ opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 },
{ opacity: '1', transform: 'none', offset: 1 }];
2018-10-23 01:05:09 +03:00
elem.animate(keyframes, {
duration: 160,
iterations: 1,
easing: 'ease-out'
});
2018-10-23 01:05:09 +03:00
}
function triggerBeforeTabChange(tabs, index, previousIndex) {
2020-05-04 12:44:12 +02:00
tabs.dispatchEvent(new CustomEvent('beforetabchange', {
2018-10-23 01:05:09 +03:00
detail: {
selectedTabIndex: index,
previousIndex: previousIndex
}
}));
if (previousIndex != null && previousIndex !== index) {
removeActivePanelClass(tabs, previousIndex);
}
2020-07-17 10:33:31 +02:00
const newPanel = getTabPanel(tabs, index);
if (newPanel) {
// animate new panel ?
if (newPanel.animate) {
fadeInRight(newPanel);
}
newPanel.classList.add('is-active');
}
2018-10-23 01:05:09 +03:00
}
function onClick(e) {
2020-07-11 11:23:28 +01:00
const tabs = this;
2020-07-11 11:23:28 +01:00
const current = tabs.querySelector('.' + activeButtonClass);
const tabButton = dom.parentWithClass(e.target, buttonClass);
2018-10-23 01:05:09 +03:00
if (tabButton && tabButton !== current) {
if (current) {
current.classList.remove(activeButtonClass);
}
2020-07-11 11:23:28 +01:00
const previousIndex = current ? parseInt(current.getAttribute('data-index')) : null;
2020-08-18 20:31:10 +02:00
setActiveTabButton(tabButton);
2020-07-11 11:23:28 +01:00
const index = parseInt(tabButton.getAttribute('data-index'));
triggerBeforeTabChange(tabs, index, previousIndex);
// If toCenter is called syncronously within the click event, it sometimes ends up canceling it
setTimeout(function () {
tabs.selectedTabIndex = index;
2020-05-04 12:44:12 +02:00
tabs.dispatchEvent(new CustomEvent('tabchange', {
2018-10-23 01:05:09 +03:00
detail: {
selectedTabIndex: index,
previousIndex: previousIndex
}
}));
}, 120);
if (tabs.scroller) {
tabs.scroller.toCenter(tabButton, false);
}
2018-10-23 01:05:09 +03:00
}
}
function onFocusOut(e) {
const parentContainer = e.target.parentNode;
const previousFocus = parentContainer.querySelector('.lastFocused');
if (previousFocus) {
previousFocus.classList.remove('lastFocused');
}
e.target.classList.add('lastFocused');
}
2018-10-23 01:05:09 +03:00
function initScroller(tabs) {
if (tabs.scroller) {
return;
}
2020-07-11 11:23:28 +01:00
const contentScrollSlider = tabs.querySelector('.emby-tabs-slider');
if (contentScrollSlider) {
tabs.scroller = new scroller(tabs, {
2018-10-23 01:05:09 +03:00
horizontal: 1,
itemNav: 0,
mouseDragging: 1,
touchDragging: 1,
slidee: contentScrollSlider,
smart: true,
releaseSwing: true,
2018-10-23 01:05:09 +03:00
scrollBy: 200,
speed: 120,
elasticBounds: 1,
dragHandle: 1,
dynamicHandle: 1,
clickBar: 1,
hiddenScroll: true,
// In safari the transform is causing the headers to occasionally disappear or flicker
2018-10-23 01:05:09 +03:00
requireAnimation: !browser.safari,
allowNativeSmoothScroll: true
});
tabs.scroller.init();
} else {
tabs.classList.add('scrollX');
tabs.classList.add('hiddenScrollX');
tabs.classList.add('smoothScrollX');
}
2018-10-23 01:05:09 +03:00
}
2020-08-16 20:24:45 +02:00
class EmbyTabs extends HTMLDivElement {
createdCallback() {
if (this.classList.contains('emby-tabs')) {
return;
}
this.classList.add('emby-tabs');
this.classList.add('focusable');
2020-08-16 20:24:45 +02:00
dom.addEventListener(this, 'click', onClick, {
passive: true
});
2020-08-16 20:24:45 +02:00
dom.addEventListener(this, 'focusout', onFocusOut);
}
2020-08-16 20:24:45 +02:00
focus() {
const selected = this.querySelector('.' + activeButtonClass);
2020-08-16 20:24:45 +02:00
if (this.lastFocused) {
focusManager.focus(this.lastFocused);
} else if (this.selectedTab) {
focusManager.focus(this.selectedTab);
} else {
focusManager.autoFocus(this);
}
}
2020-08-16 20:24:45 +02:00
refresh() {
if (this.scroller) {
this.scroller.reload();
}
}
2020-08-16 20:24:45 +02:00
attachedCallback() {
console.warn(this);
initScroller(this);
2020-08-16 20:24:45 +02:00
const current = this.querySelector('.' + activeButtonClass);
const currentIndex = current ? parseInt(current.getAttribute('data-index')) : parseInt(this.getAttribute('data-index') || '0');
2020-08-16 20:24:45 +02:00
if (currentIndex !== -1) {
this.selectedTabIndex = currentIndex;
2020-08-16 20:24:45 +02:00
const tabButtons = this.querySelectorAll('.' + buttonClass);
2020-08-16 20:24:45 +02:00
const newTabButton = tabButtons[currentIndex];
2020-08-16 20:24:45 +02:00
if (newTabButton) {
setActiveTabButton(newTabButton);
}
}
if (!this.readyFired) {
this.readyFired = true;
this.dispatchEvent(new CustomEvent('ready', {}));
}
}
2020-08-16 20:24:45 +02:00
detachedCallback() {
if (this.scroller) {
this.scroller.destroy();
this.scroller = null;
}
dom.removeEventListener(this, 'click', onClick, {
passive: true
});
}
2020-08-16 20:24:45 +02:00
getSelectedTabButton(elem) {
return elem.querySelector('.' + activeButtonClass);
}
2020-08-16 20:24:45 +02:00
selectedIndex(selected, triggerEvent) {
const tabs = this;
2020-08-16 20:24:45 +02:00
if (selected == null) {
return tabs.selectedTabIndex || 0;
}
2020-08-16 20:24:45 +02:00
const current = tabs.selectedIndex();
2020-08-16 20:24:45 +02:00
tabs.selectedTabIndex = selected;
2020-08-16 20:24:45 +02:00
const tabButtons = tabs.querySelectorAll('.' + buttonClass);
2020-08-16 20:24:45 +02:00
if (current === selected || triggerEvent === false) {
triggerBeforeTabChange(tabs, selected, current);
2020-08-16 20:24:45 +02:00
tabs.dispatchEvent(new CustomEvent('tabchange', {
detail: {
selectedTabIndex: selected
}
}));
2020-08-16 20:24:45 +02:00
const currentTabButton = tabButtons[current];
setActiveTabButton(tabButtons[selected]);
2020-08-16 20:24:45 +02:00
if (current !== selected && currentTabButton) {
currentTabButton.classList.remove(activeButtonClass);
2018-10-23 01:05:09 +03:00
}
2020-08-16 20:24:45 +02:00
} else {
onClick.call(tabs, {
target: tabButtons[selected]
});
}
}
2020-08-16 20:24:45 +02:00
getSibling(elem, method) {
let sibling = elem[method];
2020-08-16 20:24:45 +02:00
while (sibling) {
if (sibling.classList.contains(buttonClass)) {
if (!sibling.classList.contains('hide')) {
return sibling;
}
}
2020-08-16 20:24:45 +02:00
sibling = sibling[method];
}
2020-08-16 20:24:45 +02:00
return null;
}
2020-08-16 20:24:45 +02:00
selectNext() {
const current = this.getSelectedTabButton(this);
2020-08-16 20:24:45 +02:00
const sibling = this.getSibling(current, 'nextSibling');
2020-08-16 20:24:45 +02:00
if (sibling) {
onClick.call(this, {
target: sibling
});
}
}
2020-08-16 20:24:45 +02:00
selectPrevious() {
const current = this.getSelectedTabButton(this);
2020-08-16 20:24:45 +02:00
const sibling = this.getSibling(current, 'previousSibling');
2020-08-16 20:24:45 +02:00
if (sibling) {
onClick.call(this, {
target: sibling
});
}
}
2020-08-16 20:24:45 +02:00
triggerBeforeTabChange(selected) {
const tabs = this;
2020-08-16 20:24:45 +02:00
triggerBeforeTabChange(tabs, tabs.selectedIndex());
}
2020-08-16 20:24:45 +02:00
triggerTabChange(selected) {
const tabs = this;
2020-08-16 20:24:45 +02:00
tabs.dispatchEvent(new CustomEvent('tabchange', {
detail: {
selectedTabIndex: tabs.selectedIndex()
}
}));
}
2020-08-16 20:24:45 +02:00
setTabEnabled(index, enabled) {
const btn = this.querySelector('.emby-tab-button[data-index="' + index + '"]');
2020-08-16 20:24:45 +02:00
if (enabled) {
btn.classList.remove('hide');
} else {
btn.classList.remove('add');
}
}
2020-08-16 20:24:45 +02:00
}
2020-08-16 20:24:45 +02:00
customElements.define('emby-tabs', EmbyTabs, { extends: 'div' });
/* eslint-enable indent */