1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

update tabs

This commit is contained in:
Luke Pulverenti 2016-08-14 15:58:13 -04:00
parent ce30e1391e
commit 93ebd18a5c
24 changed files with 602 additions and 316 deletions

View file

@ -212,3 +212,8 @@
opacity: 0; opacity: 0;
} }
} }
.emby-button-foreground {
position: relative;
z-index: 1;
}

View file

@ -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;
}
}

View file

@ -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'
});
});

View file

@ -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. * Slide SLIDEE by amount of pixels.
* *

View file

@ -1,9 +1,16 @@
<div id="channelsPage" data-role="page" data-dom-cache="true" class="page libraryPage channelsPage pageWithAbsoluteTabs" data-contextname="${HeaderChannels}" data-require="scripts/channels,scripts/channelslatest,scripts/sections"> <div id="channelsPage" data-role="page" data-dom-cache="true" class="page libraryPage channelsPage pageWithAbsoluteTabs" data-contextname="${HeaderChannels}" data-require="scripts/channels,scripts/channelslatest,scripts/sections">
<div class="libraryViewNav"> <div is="emby-tabs" class="libraryViewNav">
<button class="pageTabButton is-active" data-index="0">${TabLatest}</button> <div class="emby-tabs-slider">
<button class="pageTabButton" data-index="1">${TabChannels}</button> <button is="emby-button" class="emby-tab-button is-active" data-index="0">
<div class="emby-button-foreground">${TabLatest}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="1">
<div class="emby-button-foreground">${TabChannels}</div>
</button>
</div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="latestTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="latestTab" data-index="0">
<div class="latestItems"></div> <div class="latestItems"></div>
</div> </div>

View file

@ -151,63 +151,6 @@
bottom: 0; bottom: 0;
} }
.pageTabButton {
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;
}
/*.libraryViewNav .pageTabButton:hover {
background-color: transparent;
}
.libraryViewNav .pageTabButton:active {
background-color: rgba(100,100,100, 0.20);
}*/
.pageTabButton:focus {
font-weight: bold !important;
}
.pageTabButton.is-active {
color: #52B54B !important;
}
.pageTabButtonSelectionBar {
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;
}
.pageTabButton.is-active .pageTabButtonSelectionBar {
background: #52B54B;
}
.viewMenuBar, .ui-body-b .libraryViewNav { .viewMenuBar, .ui-body-b .libraryViewNav {
background-color: #222326; background-color: #222326;
color: #fff; color: #fff;

View file

@ -5,7 +5,7 @@
flex-grow: 1; flex-grow: 1;
} }
.libraryViewNav .pageTabButton { .libraryViewNav .emby-tab-button {
flex-grow: 1; flex-grow: 1;
} }
} }

View file

@ -41,16 +41,16 @@ body:not(.dashboardDocument) .mainDrawerButton {
background-color: #000; background-color: #000;
} }
.libraryViewNav .pageTabButton { .libraryViewNav .emby-tab-button {
font-weight: 400; font-weight: 400;
text-transform: none; text-transform: none;
} }
.pageTabButton.is-active .pageTabButtonSelectionBar { .emby-tab-button-selection-bar {
display: none !important; display: none !important;
} }
.libraryViewNav .pageTabButton.is-active { .libraryViewNav .emby-tab-button-active {
color: #52B54B !important; color: #52B54B !important;
} }

View file

@ -1,10 +1,20 @@
<div id="indexPage" style="outline: none;" data-role="page" data-dom-cache="true" class="page homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs" data-title="${ButtonHome}" data-backdroptype="movie,series,game,book"> <div id="indexPage" style="outline: none;" data-role="page" data-dom-cache="true" class="page homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs" data-title="${ButtonHome}" data-backdroptype="movie,series,game,book">
<div class="libraryViewNav"> <div is="emby-tabs" class="libraryViewNav">
<button class="pageTabButton is-active" data-index="0">${TabHome}</button> <div class="emby-tabs-slider">
<button class="pageTabButton" data-index="1">${TabNextUp}</button> <button is="emby-button" class="emby-tab-button emby-tab-button-active" data-index="0">
<button class="pageTabButton homeFavoritesTab" data-index="2">${TabFavorites}</button> <div class="emby-button-foreground">${TabHome}</div>
<button class="pageTabButton" data-index="3">${TabUpcoming}</button> </button>
<button is="emby-button" class="emby-tab-button" data-index="1">
<div class="emby-button-foreground">${TabNextUp}</div>
</button>
<button is="emby-button" class="emby-tab-button homeFavoritesTab" data-index="2">
<div class="emby-button-foreground">${TabFavorites}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="3">
<div class="emby-button-foreground">${TabUpcoming}</div>
</button>
</div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="homeTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="homeTab" data-index="0">
<div class="ui-bar-b readOnlyContent welcomeMessage hide" style="padding: 2em; border-radius: 10px; margin: 2em auto; font-weight: normal;"> <div class="ui-bar-b readOnlyContent welcomeMessage hide" style="padding: 2em; border-radius: 10px; margin: 2em auto; font-weight: normal;">

View file

@ -1,14 +1,25 @@
<div id="liveTvSuggestedPage" data-dom-cache="true" data-role="page" class="page libraryPage liveTvPage pageWithAbsoluteTabs" data-contextname="${HeaderLiveTv}" data-backdroptype="series,movie"> <div id="liveTvSuggestedPage" data-dom-cache="true" data-role="page" class="page libraryPage liveTvPage pageWithAbsoluteTabs" data-contextname="${HeaderLiveTv}" data-backdroptype="series,movie">
<div class="libraryViewNav"> <div is="emby-tabs" class="libraryViewNav">
<div class="contentScrollSlider"> <div class="emby-tabs-slider">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <button is="emby-button" class="emby-tab-button emby-tab-button-active" data-index="0">
<button class="pageTabButton" data-index="1">${TabGuide}</button> <div class="emby-button-foreground">${TabSuggestions}</div>
<button class="pageTabButton" data-index="2">${TabChannels}</button> </button>
<button class="pageTabButton" data-index="3">${TabRecordings}</button> <button is="emby-button" class="emby-tab-button" data-index="1">
<button class="pageTabButton" data-index="4">${TabSeries}</button> <div class="emby-button-foreground">${TabGuide}</div>
</button>
<button is="emby-button" class="emby-tab-button homeFavoritesTab" data-index="2">
<div class="emby-button-foreground">${TabChannels}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="3">
<div class="emby-button-foreground">${TabRecordings}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="4">
<div class="emby-button-foreground">${TabSeries}</div>
</button>
</div> </div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0">
<div id="activePrograms" class="homePageSection"> <div id="activePrograms" class="homePageSection">
<h1 class="listHeader">${HeaderWhatsOnTV}</h1> <h1 class="listHeader">${HeaderWhatsOnTV}</h1>

View file

@ -1,13 +1,25 @@
<div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs" data-backdroptype="movie"> <div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs" data-backdroptype="movie">
<div class="libraryViewNav"> <div is="emby-tabs" class="libraryViewNav">
<div class="contentScrollSlider"> <div class="emby-tabs-slider">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <button is="emby-button" class="emby-tab-button emby-tab-button-active" data-index="0">
<button class="pageTabButton" data-index="1">${TabMovies}</button> <div class="emby-button-foreground">${TabSuggestions}</div>
<button class="pageTabButton movieTrailersTab" data-index="2">${TabTrailers}</button> </button>
<button class="pageTabButton" data-index="3">${TabCollections}</button> <button is="emby-button" class="emby-tab-button" data-index="1">
<button class="pageTabButton" data-index="4">${TabGenres}</button> <div class="emby-button-foreground">${TabMovies}</div>
<button class="pageTabButton movieStudiosTab" data-index="5">${TabStudios}</button> </button>
<button is="emby-button" class="emby-tab-button homeFavoritesTab" data-index="2">
<div class="emby-button-foreground">${TabTrailers}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="3">
<div class="emby-button-foreground">${TabCollections}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="4">
<div class="emby-button-foreground">${TabGenres}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="5">
<div class="emby-button-foreground">${TabStudios}</div>
</button>
</div> </div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0">

View file

@ -1,16 +1,31 @@
<div id="musicRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs" data-backdroptype="musicartist"> <div id="musicRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs" data-backdroptype="musicartist">
<div class="libraryViewNav"> <div is="emby-tabs" class="libraryViewNav">
<div class="contentScrollSlider"> <div class="emby-tabs-slider">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <button is="emby-button" class="emby-tab-button emby-tab-button-active" data-index="0">
<button class="pageTabButton" data-index="1">${TabAlbums}</button> <div class="emby-button-foreground">${TabSuggestions}</div>
<button class="pageTabButton" data-index="2">${TabAlbumArtists}</button> </button>
<button class="pageTabButton" data-index="3">${TabArtists}</button> <button is="emby-button" class="emby-tab-button" data-index="1">
<button class="pageTabButton" data-index="4">${TabSongs}</button> <div class="emby-button-foreground">${TabAlbums}</div>
<button class="pageTabButton" data-index="5">${TabGenres}</button> </button>
<button class="pageTabButton" data-index="6">${TabFolders}</button> <button is="emby-button" class="emby-tab-button homeFavoritesTab" data-index="2">
<div class="emby-button-foreground">${TabAlbumArtists}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="3">
<div class="emby-button-foreground">${TabArtists}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="4">
<div class="emby-button-foreground">${TabSongs}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="5">
<div class="emby-button-foreground">${TabGenres}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="6">
<div class="emby-button-foreground">${TabFolders}</div>
</button>
</div> </div>
</div> </div>
<div class="pageTabContent is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0"> <div class="pageTabContent is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0">
<div class="homePageSection"> <div class="homePageSection">
<h1 class="listHeader">${HeaderLatestMusic}</h1> <h1 class="listHeader">${HeaderLatestMusic}</h1>

View file

@ -14,11 +14,20 @@
</div> </div>
</div> </div>
<div class="libraryViewNav headroomDisabled"> <div is="emby-tabs" class="libraryViewNav headroomDisabled">
<button class="pageTabButton is-active" data-index="0">${TabNowPlaying}</button> <div class="emby-tabs-slider">
<button class="pageTabButton" data-index="1">${TabControls}</button> <button is="emby-button" class="emby-tab-button is-active" data-index="0">
<button class="pageTabButton" data-index="2">${TabPlaylist}</button> <div class="emby-button-foreground">${TabNowPlaying}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="1">
<div class="emby-button-foreground">${TabControls}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="2">
<div class="emby-button-foreground">${TabPlaylist}</div>
</button>
</div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="nowPlayingTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="nowPlayingTab" data-index="0">
<div style="text-align:center;"> <div style="text-align:center;">
<div class="nowPlayingPageTitle" style="line-height: normal;"> <div class="nowPlayingPageTitle" style="line-height: normal;">

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'cardBuilder', 'emby-itemscontainer'], function (libraryBrowser, cardBuilder) { define(['libraryBrowser', 'cardBuilder', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, cardBuilder) {
// The base query options // The base query options
var query = { var query = {

View file

@ -1,4 +1,4 @@
define(['libraryBrowser'], function (libraryBrowser) { define(['libraryBrowser', 'emby-tabs', 'emby-button'], function (libraryBrowser) {
var defaultFirstSection = 'smalllibrarytiles'; var defaultFirstSection = 'smalllibrarytiles';
@ -343,11 +343,7 @@
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
mdlTabs.dispatchEvent(new CustomEvent("tabchange", { mdlTabs.triggerTabChange();
detail: {
selectedTabIndex: libraryBrowser.selectedTab(mdlTabs)
}
}));
} }
} }

View file

@ -1,8 +1,8 @@
define(['viewManager', 'appSettings', 'appStorage', 'apphost', 'datetime', 'itemHelper', 'mediaInfo', 'scroller', 'indicators', 'dom', 'imageLoader', 'scrollStyles'], function (viewManager, appSettings, appStorage, appHost, datetime, itemHelper, mediaInfo, scroller, indicators, dom) { define(['viewManager', 'appSettings', 'appStorage', 'apphost', 'datetime', 'itemHelper', 'mediaInfo', 'scroller', 'indicators', 'dom', 'browser', 'imageLoader', 'scrollStyles'], function (viewManager, appSettings, appStorage, appHost, datetime, itemHelper, mediaInfo, scroller, indicators, dom, browser) {
function fadeInRight(elem) { function fadeInRight(elem) {
var pct = browserInfo.mobile ? '4%' : '0.5%'; var pct = browser.mobile ? '4%' : '0.5%';
var keyframes = [ var keyframes = [
{ opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 }, { opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 },
@ -15,28 +15,6 @@
}); });
} }
function animateSelectionBar(button) {
var elem = button.querySelector('.pageTabButtonSelectionBar');
if (!elem) {
return;
}
var keyframes = [
{ transform: 'translate3d(-100%, 0, 0)', offset: 0 },
{ transform: 'none', offset: 1 }];
if (!elem.animate) {
return;
}
elem.animate(keyframes, {
duration: 120,
iterations: 1,
easing: 'ease-out'
});
}
var libraryBrowser = (function (window, document, screen) { var libraryBrowser = (function (window, document, screen) {
var pageSizeKey = 'pagesize_v4'; var pageSizeKey = 'pagesize_v4';
@ -135,35 +113,9 @@
return true; return true;
}, },
selectedTab: function (tabs, selected) {
if (selected == null) {
return tabs.selectedTabIndex || 0;
}
var current = LibraryBrowser.selectedTab(tabs);
tabs.selectedTabIndex = selected;
if (current == selected) {
tabs.dispatchEvent(new CustomEvent("beforetabchange", {
detail: {
selectedTabIndex: selected
}
}));
tabs.dispatchEvent(new CustomEvent("tabchange", {
detail: {
selectedTabIndex: selected
}
}));
} else {
var tabButtons = tabs.querySelectorAll('.pageTabButton');
tabButtons[selected].click();
}
},
configureSwipeTabs: function (ownerpage, tabs) { configureSwipeTabs: function (ownerpage, tabs) {
if (!browserInfo.touch) { if (!browser.touch) {
return; return;
} }
@ -176,18 +128,18 @@
hammertime.on('swipeleft', function (e) { hammertime.on('swipeleft', function (e) {
if (LibraryBrowser.allowSwipe(e.target)) { if (LibraryBrowser.allowSwipe(e.target)) {
var selected = parseInt(LibraryBrowser.selectedTab(tabs) || '0'); var selected = parseInt(tabs.selectedIndex() || '0');
if (selected < (pageCount - 1)) { if (selected < (pageCount - 1)) {
LibraryBrowser.selectedTab(tabs, selected + 1); tabs.selectedIndex(selected + 1);
} }
} }
}); });
hammertime.on('swiperight', function (e) { hammertime.on('swiperight', function (e) {
if (LibraryBrowser.allowSwipe(e.target)) { if (LibraryBrowser.allowSwipe(e.target)) {
var selected = parseInt(LibraryBrowser.selectedTab(tabs) || '0'); var selected = parseInt(tabs.selectedIndex() || '0');
if (selected > 0) { if (selected > 0) {
LibraryBrowser.selectedTab(tabs, selected - 1); tabs.selectedIndex(selected - 1);
} }
} }
}); });
@ -196,150 +148,62 @@
configurePaperLibraryTabs: function (ownerpage, tabs, panels, animateTabs) { configurePaperLibraryTabs: function (ownerpage, tabs, panels, animateTabs) {
if (!browserInfo.safari) { if (!browser.safari) {
LibraryBrowser.configureSwipeTabs(ownerpage, tabs); LibraryBrowser.configureSwipeTabs(ownerpage, tabs);
} }
if (!browserInfo.safari || !AppInfo.isNativeApp) { ownerpage.addEventListener('viewshow', LibraryBrowser.onTabbedpagebeforeshow);
var buttons = tabs.querySelectorAll('.pageTabButton');
for (var i = 0, length = buttons.length; i < length; i++) {
var div = document.createElement('div');
div.classList.add('pageTabButtonSelectionBar');
buttons[i].appendChild(div);
}
}
tabs.addEventListener('click', function (e) { tabs.addEventListener('beforetabchange', function(e) {
if (e.detail.previousIndex != null) {
var current = tabs.querySelector('.is-active'); panels[e.detail.previousIndex].classList.remove('is-active');
var link = dom.parentWithClass(e.target, 'pageTabButton');
if (link && link != current) {
if (current) {
current.classList.remove('is-active');
panels[parseInt(current.getAttribute('data-index'))].classList.remove('is-active');
}
link.classList.add('is-active');
animateSelectionBar(link);
var index = parseInt(link.getAttribute('data-index'));
var newPanel = panels[index];
tabs.dispatchEvent(new CustomEvent("beforetabchange", {
detail: {
selectedTabIndex: index
}
}));
// If toCenter is called syncronously within the click event, it sometimes ends up canceling it
setTimeout(function () {
if (animateTabs && animateTabs.indexOf(index) != -1 && /*browserInfo.animate &&*/ newPanel.animate) {
fadeInRight(newPanel);
}
tabs.selectedTabIndex = index;
tabs.dispatchEvent(new CustomEvent("tabchange", {
detail: {
selectedTabIndex: index
}
}));
newPanel.classList.add('is-active');
}, 120);
if (tabs.scroller) {
tabs.scroller.toCenter(link, false);
}
} }
}); });
ownerpage.addEventListener('viewbeforeshow', LibraryBrowser.onTabbedpagebeforeshow); tabs.addEventListener('beforetabchange', function (e) {
var contentScrollSlider = tabs.querySelector('.contentScrollSlider'); var newPanel = panels[e.detail.selectedTabIndex];
if (contentScrollSlider) {
tabs.scroller = new scroller(tabs, {
horizontal: 1,
itemNav: 0,
mouseDragging: 1,
touchDragging: 1,
slidee: tabs.querySelector('.contentScrollSlider'),
smart: true,
releaseSwing: true,
scrollBy: 200,
speed: 120,
elasticBounds: 1,
dragHandle: 1,
dynamicHandle: 1,
clickBar: 1,
//centerOffset: window.innerWidth * .05,
hiddenScroll: true,
// In safari the transform is causing the headers to occasionally disappear or flicker if (e.detail.previousIndex != null && e.detail.previousIndex != e.detail.selectedTabIndex) {
requireAnimation: !browserInfo.safari if (newPanel.animate && (animateTabs || []).indexOf(e.detail.selectedTabIndex) != -1) {
}); fadeInRight(newPanel);
tabs.scroller.init(); }
} else { }
tabs.classList.add('hiddenScrollX');
} newPanel.classList.add('is-active');
});
}, },
onTabbedpagebeforeshow: function (e) { onTabbedpagebeforeshow: function (e) {
var page = e.target; var page = e.target;
var delay = 0; var delay = 0;
var isFirstLoad = false;
if (!page.getAttribute('data-firstload')) { var pageTabsContainer = page.querySelector('.libraryViewNav');
if (!pageTabsContainer || !pageTabsContainer.triggerTabChange) {
delay = 300; delay = 300;
isFirstLoad = true;
page.setAttribute('data-firstload', '1');
} }
if (delay) { if (delay) {
setTimeout(function () { setTimeout(function () {
LibraryBrowser.onTabbedpagebeforeshowInternal(page, e, isFirstLoad); LibraryBrowser.onTabbedpagebeforeshowInternal(page, e);
}, delay); }, delay);
} else { } else {
LibraryBrowser.onTabbedpagebeforeshowInternal(page, e, isFirstLoad); LibraryBrowser.onTabbedpagebeforeshowInternal(page, e);
} }
}, },
onTabbedpagebeforeshowInternal: function (page, e, isFirstLoad) { onTabbedpagebeforeshowInternal: function (page, e) {
var pageTabsContainer = page.querySelector('.libraryViewNav'); var pageTabsContainer = page.querySelector('.libraryViewNav');
if (isFirstLoad) { // Go back to the first tab
if (!e.detail.isRestored) {
console.log('selected tab is null, checking query string'); pageTabsContainer.selectedIndex(0);
return;
var selected = page.firstTabIndex != null ? page.firstTabIndex : parseInt(getParameterByName('tab') || '0');
console.log('selected tab will be ' + selected);
LibraryBrowser.selectedTab(pageTabsContainer, selected);
} else {
// Go back to the first tab
if (!e.detail.isRestored) {
LibraryBrowser.selectedTab(pageTabsContainer, 0);
return;
}
pageTabsContainer.dispatchEvent(new CustomEvent("beforetabchange", {
detail: {
selectedTabIndex: LibraryBrowser.selectedTab(pageTabsContainer)
}
}));
pageTabsContainer.dispatchEvent(new CustomEvent("tabchange", {
detail: {
selectedTabIndex: LibraryBrowser.selectedTab(pageTabsContainer)
}
}));
} }
pageTabsContainer.triggerTabChange();
}, },
showTab: function (url, index) { showTab: function (url, index) {
@ -814,7 +678,7 @@
document.body.appendChild(dlg); document.body.appendChild(dlg);
// Seeing an issue in Firefox and IE where it's initially visible in the bottom right, then moves to the center // Seeing an issue in Firefox and IE where it's initially visible in the bottom right, then moves to the center
var delay = browserInfo.animate ? 0 : 100; var delay = browser.animate ? 0 : 100;
setTimeout(function () { setTimeout(function () {
dialogHelper.open(dlg); dialogHelper.open(dlg);
}, delay); }, delay);

View file

@ -645,32 +645,30 @@
viewMenuBarTabs.classList.remove('hide'); viewMenuBarTabs.classList.remove('hide');
} }
if (LibraryMenu.tabType != type) { require(['emby-tabs', 'emby-button'], function () {
if (LibraryMenu.tabType != type) {
var index = 0; var index = 0;
viewMenuBarTabs.innerHTML = '<div class="libraryViewNav hiddenScrollX">' + builder().map(function (t) { viewMenuBarTabs.innerHTML = '<div is="emby-tabs"><div class="emby-tabs-slider">' + builder().map(function (t) {
var tabClass = selectedIndex == index ? 'pageTabButton is-active' : 'pageTabButton'; var tabClass = selectedIndex == index ? 'emby-tab-button emby-tab-button-active' : 'emby-tab-button';
var tabHtml = '<a class="' + tabClass + '" href="' + t.href + '" data-index="' + index + '">' + t.name + '<div class="pageTabButtonSelectionBar"></div></a>'; var tabHtml = '<button onclick="Dashboard.navigate(this.getAttribute(\'data-href\'));" data-href="' + t.href + '" is="emby-button" class="' + tabClass + '" data-index="' + index + '"><div class="emby-button-foreground">' + t.name + '</div></button>';
index++; index++;
return tabHtml; return tabHtml;
}).join('') + '</div>'; }).join('') + '</div></div>';
document.body.classList.add('withTallToolbar'); document.body.classList.add('withTallToolbar');
LibraryMenu.tabType = type;
return;
}
viewMenuBarTabs.querySelector('is=["emby-tabs"]').selectedIndex(selectedIndex);
LibraryMenu.tabType = type; LibraryMenu.tabType = type;
return; });
}
var activeTab = viewMenuBarTabs.querySelector('.is-active');
var newTab = viewMenuBarTabs.querySelector('.pageTabButton[data-index="' + selectedIndex + '"]');
newTab.classList.add('is-active');
if (newTab != activeTab && activeTab) {
activeTab.classList.remove('is-active');
}
LibraryMenu.tabType = type;
}, },
setTitle: function (title) { setTitle: function (title) {

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer'], function (libraryBrowser, cardBuilder) { define(['libraryBrowser', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, cardBuilder) {
function enableScrollX() { function enableScrollX() {
return browserInfo.mobile && AppInfo.enableAppLayouts; return browserInfo.mobile && AppInfo.enableAppLayouts;

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer'], function (libraryBrowser, categorysyncbuttons, cardBuilder) { define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, categorysyncbuttons, cardBuilder) {
function enableScrollX() { function enableScrollX() {
return browserInfo.mobile && AppInfo.enableAppLayouts; return browserInfo.mobile && AppInfo.enableAppLayouts;
@ -323,11 +323,7 @@
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
renderedTabs = []; renderedTabs = [];
mdlTabs.dispatchEvent(new CustomEvent("tabchange", { mdlTabs.triggerTabChange();
detail: {
selectedTabIndex: libraryBrowser.selectedTab(mdlTabs)
}
}));
} }
} }

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer'], function (libraryBrowser, cardBuilder) { define(['libraryBrowser', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, cardBuilder) {
function itemsPerRow() { function itemsPerRow() {

View file

@ -1,4 +1,4 @@
define(['components/remotecontrol'], function (remotecontrolFactory) { define(['components/remotecontrol', 'emby-tabs', 'emby-button'], function (remotecontrolFactory) {
return function (view, params) { return function (view, params) {

View file

@ -1268,6 +1268,7 @@ var AppInfo = {};
define("emby-collapse", [embyWebComponentsBowerPath + "/emby-collapse/emby-collapse"], returnFirstDependency); define("emby-collapse", [embyWebComponentsBowerPath + "/emby-collapse/emby-collapse"], returnFirstDependency);
define("emby-button", [embyWebComponentsBowerPath + "/emby-button/emby-button"], returnFirstDependency); define("emby-button", [embyWebComponentsBowerPath + "/emby-button/emby-button"], returnFirstDependency);
define("emby-itemscontainer", [embyWebComponentsBowerPath + "/emby-itemscontainer/emby-itemscontainer"], returnFirstDependency); define("emby-itemscontainer", [embyWebComponentsBowerPath + "/emby-itemscontainer/emby-itemscontainer"], returnFirstDependency);
define("emby-tabs", [embyWebComponentsBowerPath + "/emby-tabs/emby-tabs"], returnFirstDependency);
define("itemHoverMenu", [embyWebComponentsBowerPath + "/itemhovermenu/itemhovermenu"], returnFirstDependency); define("itemHoverMenu", [embyWebComponentsBowerPath + "/itemhovermenu/itemhovermenu"], returnFirstDependency);
define("multiSelect", [embyWebComponentsBowerPath + "/multiselect/multiselect"], returnFirstDependency); define("multiSelect", [embyWebComponentsBowerPath + "/multiselect/multiselect"], returnFirstDependency);
define("alphaPicker", [embyWebComponentsBowerPath + "/alphapicker/alphapicker"], returnFirstDependency); define("alphaPicker", [embyWebComponentsBowerPath + "/alphapicker/alphapicker"], returnFirstDependency);

View file

@ -1,4 +1,4 @@
define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer'], function (libraryBrowser, categorysyncbuttons, cardBuilder) { define(['libraryBrowser', 'components/categorysyncbuttons', 'cardBuilder', 'scrollStyles', 'emby-itemscontainer', 'emby-tabs', 'emby-button'], function (libraryBrowser, categorysyncbuttons, cardBuilder) {
return function (view, params) { return function (view, params) {
@ -209,11 +209,7 @@
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') {
renderedTabs = []; renderedTabs = [];
mdlTabs.dispatchEvent(new CustomEvent("tabchange", { mdlTabs.triggerTabChange();
detail: {
selectedTabIndex: libraryBrowser.selectedTab(mdlTabs)
}
}));
} }
} }

View file

@ -1,14 +1,28 @@
<div id="tvRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs" data-backdroptype="series"> <div id="tvRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs" data-backdroptype="series">
<div class="libraryViewNav"> <div is="emby-tabs" class="libraryViewNav">
<div class="contentScrollSlider"> <div class="emby-tabs-slider">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <button is="emby-button" class="emby-tab-button emby-tab-button-active" data-index="0">
<button class="pageTabButton" data-index="1">${TabLatest}</button> <div class="emby-button-foreground">${TabSuggestions}</div>
<button class="pageTabButton" data-index="2">${TabUpcoming}</button> </button>
<button class="pageTabButton" data-index="3">${TabShows}</button> <button is="emby-button" class="emby-tab-button" data-index="1">
<button class="pageTabButton tvEpisodesTab" data-index="4">${TabEpisodes}</button> <div class="emby-button-foreground">${TabLatest}</div>
<button class="pageTabButton" data-index="5">${TabGenres}</button> </button>
<button class="pageTabButton" data-index="6">${TabNetworks}</button> <button is="emby-button" class="emby-tab-button" data-index="2">
<div class="emby-button-foreground">${TabUpcoming}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="3">
<div class="emby-button-foreground">${TabShows}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="4">
<div class="emby-button-foreground">${TabEpisodes}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="5">
<div class="emby-button-foreground">${TabGenres}</div>
</button>
<button is="emby-button" class="emby-tab-button" data-index="6">
<div class="emby-button-foreground">${TabNetworks}</div>
</button>
</div> </div>
</div> </div>