diff --git a/dashboard-ui/bower_components/emby-webcomponents/emby-button/emby-button.css b/dashboard-ui/bower_components/emby-webcomponents/emby-button/emby-button.css index 0df4212d84..38d7ad3604 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/emby-button/emby-button.css +++ b/dashboard-ui/bower_components/emby-webcomponents/emby-button/emby-button.css @@ -212,3 +212,8 @@ opacity: 0; } } + +.emby-button-foreground { + position: relative; + z-index: 1; +} diff --git a/dashboard-ui/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css b/dashboard-ui/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css new file mode 100644 index 0000000000..1ffd6cdf0d --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/emby-tabs/emby-tabs.css @@ -0,0 +1,94 @@ +.emby-tab-button { + background: transparent; + border: 0 !important; + cursor: pointer; + outline: none !important; + width: auto; + font-family: inherit; + font-size: inherit; + color: #aaa !important; + display: inline-block; + vertical-align: middle; + flex-shrink: 0; + margin: 0; + padding: 1.2em .9em; + transition: none !important; + position: relative; + text-transform: uppercase; + font-weight: bold !important; + height: auto; + min-width: initial; + line-height: initial; + border-radius: 0 !important; + overflow: hidden; +} + + .emby-tab-button:focus { + font-weight: bold !important; + } + +.emby-tab-button-active { + color: #52B54B !important; +} + +.emby-tabs-slider { + position: relative; +} + +.emby-tabs-selection-bar { + position: absolute; + left: 0; + /* Need this or it will be partially covered by the drop-shadow on android */ + bottom: 1px; + height: 2px; + z-index: 1000; + background: #52B54B; + width: 0; +} + +.emby-tab-button-selection-bar { + position: absolute; + left: 0; + border: 0; + /* Need this or it will be partially covered by the drop-shadow on android */ + bottom: 1px; + height: 2px; + right: 0; + border-radius: 0; + z-index: 1000; +} + +.emby-tab-button-selection-bar-active { + background: #52B54B; +} + +.emby-tab-button-ripple-effect { + position: absolute !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + width: auto !important; + height: auto !important; + background: #181818 !important; + animation: emby-tab-button-ripple-animation .8s !important; + transform-origin: center center !important; + border-radius: 0; +} + +@keyframes emby-tab-button-ripple-animation { + 0% { + transform: scale(.2, 1); + opacity: 0.5; + } + + 50% { + transform: none; + opacity: 1; + } + + 100% { + transform: none; + opacity: 0; + } +} diff --git a/dashboard-ui/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js b/dashboard-ui/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js new file mode 100644 index 0000000000..4df2fada52 --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/emby-tabs/emby-tabs.js @@ -0,0 +1,309 @@ +define(['dom', 'scroller', 'browser', 'registerElement', 'css!./emby-tabs', 'scrollStyles'], function (dom, scroller, browser) { + + var EmbyTabs = Object.create(HTMLDivElement.prototype); + var buttonClass = 'emby-tab-button'; + var activeButtonClass = buttonClass + '-active'; + + function getBoundingClientRect(elem) { + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if (elem.getBoundingClientRect) { + return elem.getBoundingClientRect(); + } else { + return { top: 0, left: 0 }; + } + } + + function getButtonSelectionBar(tabButton) { + var elem = tabButton.querySelector('.' + buttonClass + '-selection-bar'); + + if (!elem) { + elem = document.createElement('div'); + elem.classList.add(buttonClass + '-selection-bar'); + tabButton.appendChild(elem); + } + + return elem; + } + + function hideButtonSelectionBar(tabButton) { + + var elem = getButtonSelectionBar(tabButton); + + elem.classList.add('hide'); + elem.classList.remove('emby-tab-button-selection-bar-active'); + } + + function showButtonSelectionBar(tabButton) { + var elem = getButtonSelectionBar(tabButton); + + elem.classList.remove('hide'); + elem.classList.add('emby-tab-button-selection-bar-active'); + } + + function animtateSelectionBar(bar, start, pos, duration) { + + var endTransform = pos ? ('translateX(' + pos + 'px)') : 'none'; + var startTransform = start ? ('translateX(' + start + 'px)') : 'none'; + + if (!duration || !bar.animate) { + bar.style.transform = endTransform; + return; + } + + bar.style.transform = startTransform; + + var keyframes = [ + { transform: 'translateX(' + start + 'px)', offset: 0 }, + { transform: endTransform, offset: 1 }]; + + bar.animate(keyframes, { + duration: duration, + iterations: 1, + easing: 'ease-out', + fill: 'forwards' + }); + } + + function moveSelectionBar(tabs, newButton, oldButton, animate) { + + if (oldButton) { + hideButtonSelectionBar(oldButton); + } + hideButtonSelectionBar(newButton); + + var selectionBar = tabs.selectionBar; + + if (selectionBar) { + selectionBar.style.width = newButton.offsetWidth + 'px'; + selectionBar.classList.remove('hide'); + } + + var tabsOffset = getBoundingClientRect(tabs); + var startOffset = tabs.currentOffset || 0; + + if (oldButton) { + if (tabs.scroller) { + startOffset = tabs.scroller.getCenterPosition(oldButton); + } else { + startOffset = getBoundingClientRect(oldButton).left - tabsOffset.left; + } + } + + var endPosition; + if (tabs.scroller) { + endPosition = tabs.scroller.getCenterPosition(newButton); + } else { + var tabButtonOffset = getBoundingClientRect(newButton); + endPosition = tabButtonOffset.left - tabsOffset.left; + } + + var delay = animate ? 180 : 0; + if (selectionBar) { + animtateSelectionBar(selectionBar, startOffset, endPosition, delay); + } + tabs.currentOffset = endPosition; + + newButton.classList.add(activeButtonClass); + + setTimeout(function () { + + showButtonSelectionBar(newButton); + if (selectionBar) { + selectionBar.classList.add('hide'); + } + + }, delay); + } + + function onClick(e) { + + var tabs = this; + + var current = tabs.querySelector('.' + activeButtonClass); + var tabButton = dom.parentWithClass(e.target, buttonClass); + + if (tabButton && tabButton != current) { + + if (current) { + current.classList.remove(activeButtonClass); + } + + var previousIndex = current ? parseInt(current.getAttribute('data-index')) : null; + + moveSelectionBar(tabs, tabButton, current, true); + var index = parseInt(tabButton.getAttribute('data-index')); + + tabs.dispatchEvent(new CustomEvent("beforetabchange", { + detail: { + selectedTabIndex: index, + previousIndex: previousIndex + } + })); + + // If toCenter is called syncronously within the click event, it sometimes ends up canceling it + setTimeout(function () { + + tabs.selectedTabIndex = index; + + tabs.dispatchEvent(new CustomEvent("tabchange", { + detail: { + selectedTabIndex: index, + previousIndex: previousIndex + } + })); + }, 120); + + if (tabs.scroller) { + tabs.scroller.toCenter(tabButton, false); + } + + } + } + + function initScroller(tabs) { + + if (tabs.scroller) { + return; + } + + var contentScrollSlider = tabs.querySelector('.emby-tabs-slider'); + if (contentScrollSlider) { + tabs.scroller = new scroller(tabs, { + horizontal: 1, + itemNav: 0, + mouseDragging: 1, + touchDragging: 1, + slidee: contentScrollSlider, + smart: true, + releaseSwing: true, + 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 + requireAnimation: !browser.safari + }); + tabs.scroller.init(); + } else { + tabs.classList.add('hiddenScrollX'); + } + } + + function initSelectionBar(tabs) { + + var contentScrollSlider = tabs.querySelector('.emby-tabs-slider'); + + if (!contentScrollSlider) { + return; + } + + var elem = document.createElement('div'); + elem.classList.add('emby-tabs-selection-bar'); + + contentScrollSlider.appendChild(elem); + tabs.selectionBar = elem; + } + + EmbyTabs.createdCallback = function () { + + if (this.classList.contains('emby-tabs')) { + return; + } + this.classList.add('emby-tabs'); + + dom.addEventListener(this, 'click', onClick, { + passive: true + }); + + initSelectionBar(this); + }; + + EmbyTabs.attachedCallback = function () { + + initScroller(this); + + var current = this.querySelector('.' + activeButtonClass); + var currentIndex = current ? parseInt(current.getAttribute('data-index')) : 0; + + var newTabButton = this.querySelectorAll('.' + buttonClass)[currentIndex]; + + if (newTabButton) { + moveSelectionBar(this, newTabButton, current, false); + } + }; + + EmbyTabs.detachedCallback = function () { + + if (this.scroller) { + this.scroller.destroy(); + this.scroller = null; + } + + dom.removeEventListener(this, 'click', onClick, { + passive: true + }); + this.selectionBar = null; + }; + + EmbyTabs.selectedIndex = function (selected) { + + var tabs = this; + + if (selected == null) { + + return tabs.selectedTabIndex || 0; + } + + var current = tabs.selectedIndex(); + + tabs.selectedTabIndex = selected; + + var tabButtons = tabs.querySelectorAll('.' + buttonClass); + + if (current == selected) { + + tabs.dispatchEvent(new CustomEvent("beforetabchange", { + detail: { + selectedTabIndex: selected + } + })); + tabs.dispatchEvent(new CustomEvent("tabchange", { + detail: { + selectedTabIndex: selected + } + })); + + moveSelectionBar(tabs, tabButtons[selected], tabButtons[selected], false); + } else { + tabButtons[selected].click(); + } + }; + + EmbyTabs.triggerTabChange = function (selected) { + + var tabs = this; + + tabs.dispatchEvent(new CustomEvent("beforetabchange", { + detail: { + selectedTabIndex: tabs.selectedIndex() + } + })); + + tabs.dispatchEvent(new CustomEvent("tabchange", { + detail: { + selectedTabIndex: tabs.selectedIndex() + } + })); + }; + + document.registerElement('emby-tabs', { + prototype: EmbyTabs, + extends: 'div' + }); +}); \ No newline at end of file diff --git a/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js b/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js index 995a8e372b..6f66bbe0fc 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js +++ b/dashboard-ui/bower_components/emby-webcomponents/scroller/smoothscroller.js @@ -475,6 +475,12 @@ define(['browser', 'layoutManager', 'dom', 'scrollStyles'], function (browser, l }; }; + self.getCenterPosition = function(item) { + + var pos = self.getPos(item); + return within(pos.center, pos.start, pos.end); + }; + /** * Slide SLIDEE by amount of pixels. * diff --git a/dashboard-ui/channels.html b/dashboard-ui/channels.html index 049b89f7c2..4e442131fe 100644 --- a/dashboard-ui/channels.html +++ b/dashboard-ui/channels.html @@ -1,9 +1,16 @@