Use float for item details poster (#7195)

* Use float for item details poster

* Add list view children to primary content

* Move additional sections to primary container

* Add series to list view children

* Fix order of primary content sections
This commit is contained in:
Bill Thornton
2025-10-17 16:42:40 -04:00
committed by GitHub
parent 1ed047df3d
commit 4fa5176982
10 changed files with 243 additions and 190 deletions

View File

@@ -4,172 +4,186 @@
<div class="detailLogo"></div>
<div class="detailPageWrapperContainer">
<div class="detailPagePrimaryContainer padded-left padded-right">
<div class="infoWrapper">
<div class="detailImageContainer padded-left"></div>
<div class="nameContainer"></div>
<div class="itemMiscInfo itemMiscInfo-primary" style="margin-bottom: 0.6em;"></div>
<div class="itemMiscInfo itemMiscInfo-secondary" style="margin-bottom: 0.6em;"></div>
<div class="detailPagePrimaryContainer">
<div class="detailImageContainer hide-mobile"></div>
<div class="detailRibbon padded-left padded-right">
<div class="infoWrapper">
<div class="detailImageContainer hide-desktop hide-tv"></div>
<div class="nameContainer"></div>
<div class="itemMiscInfo itemMiscInfo-primary" style="margin-bottom: 0.6em;"></div>
<div class="itemMiscInfo itemMiscInfo-secondary" style="margin-bottom: 0.6em;"></div>
</div>
<div class="mainDetailButtons focuscontainer-x">
<button is="emby-button" type="button" class="button-flat btnPlay hide detailButton" title="${ButtonResume}" data-action="resume">
<div class="detailButton-content">
<span class="material-icons detailButton-icon play_arrow" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnReplay hide detailButton" title="${Play}" data-action="play">
<div class="detailButton-content">
<span class="material-icons detailButton-icon replay" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnDownload hide detailButton" title="${Download}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon get_app" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnPlayTrailer hide detailButton" title="${ButtonTrailer}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon theaters" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnInstantMix hide detailButton" title="${HeaderInstantMix}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon explore" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnShuffle hide detailButton" title="${Shuffle}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon shuffle" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnCancelSeriesTimer hide detailButton" title="${CancelSeries}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon delete" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnCancelTimer hide detailButton" title="${StopRecording}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon stop" aria-hidden="true"></span>
</div>
</button>
<button is="emby-playstatebutton" type="button" class="button-flat btnPlaystate hide detailButton" title="">
<div class="detailButton-content">
<span class="material-icons detailButton-icon check" aria-hidden="true"></span>
</div>
</button>
<button is="emby-ratingbutton" type="button" class="button-flat btnUserRating hide detailButton" title="${Rate}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon favorite" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnSplitVersions hide detailButton" title="${ButtonSplit}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon call_split" aria-hidden="true"></span>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnMoreCommands hide detailButton" title="${ButtonMore}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon more_vert" aria-hidden="true"></span>
</div>
</button>
</div>
</div>
<div class="mainDetailButtons focuscontainer-x">
<button is="emby-button" type="button" class="button-flat btnPlay hide detailButton" title="${ButtonResume}" data-action="resume">
<div class="detailButton-content">
<span class="material-icons detailButton-icon play_arrow" aria-hidden="true"></span>
</div>
</button>
<div class="detailPagePrimaryContent padded-right">
<div class="detailSection">
<form class="trackSelections hide focuscontainer-x">
<div class="selectContainer selectSourceContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectSource detailTrackSelect" label=""></select>
</div>
<div class="selectContainer selectVideoContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectVideo detailTrackSelect" label=""></select>
</div>
<div class="selectContainer selectAudioContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectAudio detailTrackSelect" label=""></select>
</div>
<div class="selectContainer selectSubtitlesContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectSubtitles detailTrackSelect" label=""></select>
</div>
</form>
<button is="emby-button" type="button" class="button-flat btnReplay hide detailButton" title="${Play}" data-action="play">
<div class="detailButton-content">
<span class="material-icons detailButton-icon replay" aria-hidden="true"></span>
</div>
</button>
<div class="recordingFields hide" style="margin: 0.5em 0 1.5em;"></div>
<button is="emby-button" type="button" class="button-flat btnDownload hide detailButton" title="${Download}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon get_app" aria-hidden="true"></span>
</div>
</button>
<div class="detailSectionContent">
<p class="itemGenres"></p>
<h3 class="tagline"></h3>
<p class="overview"></p>
<div class="overview-controls">
<a class="overview-expand hide" is="emby-linkbutton" href="#">${ShowMore}</a>
</div>
<p id="itemBirthday"></p>
<p id="itemBirthLocation"></p>
<p id="itemDeathDate"></p>
<p id="seriesAirTime"></p>
<button is="emby-button" type="button" class="button-flat btnPlayTrailer hide detailButton" title="${ButtonTrailer}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon theaters" aria-hidden="true"></span>
<div class="itemTags focuscontainer-x hide" style="margin: 0.7em 0; font-size: 92%;"></div>
<div class="itemExternalLinks focuscontainer-x hide" style="margin: 0.7em 0; font-size: 92%;"></div>
<div class="seriesRecordingEditor"></div>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnInstantMix hide detailButton" title="${HeaderInstantMix}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon explore" aria-hidden="true"></span>
</div>
</button>
<div class="itemDetailsGroup">
<div class="detailsGroupItem genresGroup hide">
<div class="genresLabel label"></div>
<div class="genres content focuscontainer-x"></div>
</div>
<button is="emby-button" type="button" class="button-flat btnShuffle hide detailButton" title="${Shuffle}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon shuffle" aria-hidden="true"></span>
</div>
</button>
<div class="detailsGroupItem directorsGroup hide">
<div class="directorsLabel label"></div>
<div class="directors content focuscontainer-x"></div>
</div>
<button is="emby-button" type="button" class="button-flat btnCancelSeriesTimer hide detailButton" title="${CancelSeries}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon delete" aria-hidden="true"></span>
</div>
</button>
<div class="detailsGroupItem writersGroup hide">
<div class="writersLabel label"></div>
<div class="writers content focuscontainer-x"></div>
</div>
<button is="emby-button" type="button" class="button-flat btnCancelTimer hide detailButton" title="${StopRecording}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon stop" aria-hidden="true"></span>
<div class="detailsGroupItem studiosGroup hide">
<div class="studiosLabel label"></div>
<div class="studios content focuscontainer-x"></div>
</div>
</div>
</button>
<button is="emby-playstatebutton" type="button" class="button-flat btnPlaystate hide detailButton" title="">
<div class="detailButton-content">
<span class="material-icons detailButton-icon check" aria-hidden="true"></span>
<div id="seriesTimerScheduleSection" class="verticalSection detailVerticalSection hide" style="margin-top: -3em;">
<h2 class="sectionTitle">${Schedule}</h2>
<div id="seriesTimerSchedule" is="emby-itemscontainer" class="itemsContainer vertical-list padded-right" data-contextmenu="false"></div>
</div>
</button>
<button is="emby-ratingbutton" type="button" class="button-flat btnUserRating hide detailButton" title="${Rate}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon favorite" aria-hidden="true"></span>
</div>
</button>
<div class="collectionItems hide"></div>
<button is="emby-button" type="button" class="button-flat btnSplitVersions hide detailButton" title="${ButtonSplit}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon call_split" aria-hidden="true"></span>
<div class="nextUpSection verticalSection detailVerticalSection hide">
<h2 class="sectionTitle sectionTitle-cards">${NextUp}</h2>
<div is="emby-itemscontainer" class="nextUpItems vertical-wrap padded-right"></div>
</div>
</button>
<button is="emby-button" type="button" class="button-flat btnMoreCommands hide detailButton" title="${ButtonMore}">
<div class="detailButton-content">
<span class="material-icons detailButton-icon more_vert" aria-hidden="true"></span>
<div class="programGuideSection hide verticalSection detailVerticalSection">
<div class="programGuide"></div>
</div>
</button>
<div id="listChildrenCollapsible" class="hide verticalSection detailVerticalSection">
<h2 class="sectionTitle sectionTitle-cards hide">
<span></span>
</h2>
<div id="childrenContent">
<div is="emby-itemscontainer" class="itemsContainer padded-right" style="text-align: left;"></div>
</div>
</div>
</div>
</div>
</div>
<div class="detailPageSecondaryContainer padded-bottom-page">
<div class="detailPageContent">
<div class="detailPagePrimaryContent padded-right">
<div class="detailSection">
<form class="trackSelections hide focuscontainer-x">
<div class="selectContainer selectSourceContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectSource detailTrackSelect" label=""></select>
</div>
<div class="selectContainer selectVideoContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectVideo detailTrackSelect" label=""></select>
</div>
<div class="selectContainer selectAudioContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectAudio detailTrackSelect" label=""></select>
</div>
<div class="selectContainer selectSubtitlesContainer hide trackSelectionFieldContainer flex-shrink-zero">
<select is="emby-select" class="selectSubtitles detailTrackSelect" label=""></select>
</div>
</form>
<div class="recordingFields hide" style="margin: 0.5em 0 1.5em;"></div>
<div class="detailSectionContent">
<p class="itemGenres"></p>
<h3 class="tagline"></h3>
<p class="overview"></p>
<div class="overview-controls">
<a class="overview-expand hide" is="emby-linkbutton" href="#">${ShowMore}</a>
</div>
<p id="itemBirthday"></p>
<p id="itemBirthLocation"></p>
<p id="itemDeathDate"></p>
<p id="seriesAirTime"></p>
<div class="itemTags focuscontainer-x hide" style="margin: 0.7em 0; font-size: 92%;"></div>
<div class="itemExternalLinks focuscontainer-x hide" style="margin: 0.7em 0; font-size: 92%;"></div>
<div class="seriesRecordingEditor"></div>
</div>
<div class="itemDetailsGroup">
<div class="detailsGroupItem genresGroup hide">
<div class="genresLabel label"></div>
<div class="genres content focuscontainer-x"></div>
</div>
<div class="detailsGroupItem directorsGroup hide">
<div class="directorsLabel label"></div>
<div class="directors content focuscontainer-x"></div>
</div>
<div class="detailsGroupItem writersGroup hide">
<div class="writersLabel label"></div>
<div class="writers content focuscontainer-x"></div>
</div>
<div class="detailsGroupItem studiosGroup hide">
<div class="studiosLabel label"></div>
<div class="studios content focuscontainer-x"></div>
</div>
</div>
</div>
</div>
<div id="seriesTimerScheduleSection" class="verticalSection detailVerticalSection hide" style="margin-top: -3em;">
<h2 class="sectionTitle">${Schedule}</h2>
<div id="seriesTimerSchedule" is="emby-itemscontainer" class="itemsContainer vertical-list padded-right" data-contextmenu="false"></div>
</div>
<div class="collectionItems hide"></div>
<div class="nextUpSection verticalSection detailVerticalSection hide">
<h2 class="sectionTitle sectionTitle-cards">${NextUp}</h2>
<div is="emby-itemscontainer" class="nextUpItems vertical-wrap padded-right"></div>
</div>
<div class="programGuideSection hide verticalSection detailVerticalSection">
<div class="programGuide"></div>
</div>
<div id="childrenCollapsible" class="hide verticalSection detailVerticalSection">
<h2 class="childrenSectionHeader sectionTitle sectionTitle-cards hide">
<span id="childrenTitle"></span>
<h2 class="sectionTitle sectionTitle-cards hide">
<span></span>
</h2>
<div id="childrenContent">
<div is="emby-itemscontainer" class="childrenItemsContainer itemsContainer padded-right" style="text-align: left;"></div>
<div>
<div is="emby-itemscontainer" class="itemsContainer padded-right" style="text-align: left;"></div>
</div>
</div>

View File

@@ -45,6 +45,14 @@ import 'elements/emby-select/emby-select';
import 'styles/scrollstyles.scss';
/** Item types that use a list view for their children. */
const LIST_VIEW_TYPES = [
BaseItemKind.MusicAlbum,
BaseItemKind.Playlist,
BaseItemKind.Season,
BaseItemKind.Series
];
function autoFocus(container) {
import('../../components/autoFocuser').then(({ default: autoFocuser }) => {
autoFocuser.autoFocus(container);
@@ -554,7 +562,6 @@ function reloadFromItem(instance, page, params, item, user) {
renderBackdrop(page, item);
// Render the main information for the item
page.querySelector('.detailPagePrimaryContainer').classList.add('detailRibbon');
renderName(item, page.querySelector('.nameContainer'), params.context);
renderDetails(page, item, apiClient, params.context);
renderTrackSelections(page, instance, item);
@@ -749,12 +756,15 @@ function renderDetailImage(apiClient, elem, item, loader) {
}
function renderImage(page, item, apiClient) {
renderDetailImage(
apiClient,
page.querySelector('.detailImageContainer'),
item,
imageLoader
);
page.querySelectorAll('.detailImageContainer')
.forEach(elem => {
renderDetailImage(
apiClient,
elem,
item,
imageLoader
);
});
}
function setPeopleHeader(page, item) {
@@ -803,18 +813,22 @@ function setInitialCollapsibleState(page, item, apiClient, context, user) {
page.querySelector('.collectionItems').innerHTML = '';
if (item.Type == 'Playlist') {
page.querySelector('#childrenCollapsible').classList.remove('hide');
page.querySelector('#listChildrenCollapsible').classList.remove('hide');
page.querySelector('#childrenCollapsible').classList.add('hide');
renderPlaylistItems(page, item);
} else if (item.Type == 'Studio' || item.Type == 'Person' || item.Type == 'Genre' || item.Type == 'MusicGenre' || item.Type == 'MusicArtist') {
page.querySelector('#childrenCollapsible').classList.remove('hide');
page.querySelector('#listChildrenCollapsible').classList.remove('hide');
page.querySelector('#childrenCollapsible').classList.add('hide');
renderItemsByName(page, item);
} else if (item.IsFolder) {
if (item.Type == 'BoxSet') {
page.querySelector('#listChildrenCollapsible').classList.add('hide');
page.querySelector('#childrenCollapsible').classList.add('hide');
}
renderChildren(page, item);
} else {
page.querySelector('#listChildrenCollapsible').classList.add('hide');
page.querySelector('#childrenCollapsible').classList.add('hide');
}
@@ -1338,6 +1352,9 @@ function renderTags(page, item) {
}
function renderChildren(page, item) {
const childrenCollapsible = page.querySelector(LIST_VIEW_TYPES.includes(item.Type) ? '#listChildrenCollapsible' : '#childrenCollapsible');
const childrenItemsContainer = childrenCollapsible.querySelector('.itemsContainer');
let fields = 'ItemCounts,PrimaryImageAspectRatio,CanDelete,MediaSourceCount';
const query = {
ParentId: item.Id,
@@ -1375,7 +1392,6 @@ function renderChildren(page, item) {
let html = '';
let scrollX = false;
let isList = false;
const childrenItemsContainer = page.querySelector('.childrenItemsContainer');
if (item.Type == 'MusicAlbum') {
let showArtist = false;
@@ -1451,7 +1467,7 @@ function renderChildren(page, item) {
}
if (item.Type !== 'BoxSet') {
page.querySelector('#childrenCollapsible').classList.remove('hide');
childrenCollapsible.classList.remove('hide');
}
if (scrollX) {
childrenItemsContainer.classList.add('scrollX');
@@ -1502,21 +1518,23 @@ function renderChildren(page, item) {
}
});
let childrenTitle = globalize.translate('Items');
if (item.Type == 'Season') {
page.querySelector('#childrenTitle').innerHTML = globalize.translate('Episodes');
childrenTitle = globalize.translate('Episodes');
} else if (item.Type == 'Series') {
page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderSeasons');
childrenTitle = globalize.translate('HeaderSeasons');
} else if (item.Type == 'MusicAlbum') {
page.querySelector('#childrenTitle').innerHTML = globalize.translate('HeaderTracks');
} else {
page.querySelector('#childrenTitle').innerHTML = globalize.translate('Items');
childrenTitle = globalize.translate('HeaderTracks');
}
childrenCollapsible.querySelectorAll('.sectionTitle > span').forEach(el => {
el.innerText = childrenTitle;
});
if (item.Type == 'MusicAlbum' || item.Type == 'Season') {
page.querySelector('.childrenSectionHeader').classList.add('hide');
page.querySelector('#childrenCollapsible').classList.add('verticalSection-extrabottompadding');
childrenCollapsible.querySelector('.sectionTitle').classList.add('hide');
childrenCollapsible.classList.add('verticalSection-extrabottompadding');
} else {
page.querySelector('.childrenSectionHeader').classList.remove('hide');
childrenCollapsible.querySelector('.sectionTitle').classList.remove('hide');
}
}

View File

@@ -58,6 +58,9 @@
}
.hide,
.layout-desktop .hide-desktop,
.layout-mobile .hide-mobile,
.layout-tv .hide-tv,
.mouseIdle .hide-mouse-idle,
.mouseIdle-tv .hide-mouse-idle-tv {
display: none !important;

View File

@@ -777,7 +777,7 @@
padding-left: 0; // Reset padding for focused button since 'margin-left' is 0
}
.detailPagePrimaryContainer {
.detailRibbon {
display: flex;
align-items: center;
align-content: center;
@@ -791,6 +791,8 @@
.layout-desktop & {
position: relative;
margin-top: -7.2em;
height: 7.2em;
}
.layout-tv & {
@@ -808,16 +810,6 @@
}
}
.layout-desktop .detailRibbon {
margin-top: -7.2em;
height: 7.2em;
}
.layout-tv .detailRibbon {
margin-top: 0;
height: inherit;
}
.infoWrapper {
min-width: 0;
max-width: 100%;
@@ -843,6 +835,10 @@
max-width: 100%;
}
.detailPagePrimaryContainer {
position: relative;
}
.detailPageSecondaryContainer {
padding-top: 1.25em;
@@ -856,9 +852,8 @@
}
.detailImageContainer .card {
// important is needed here to override :focus setting
// the position to relative in the tv layout
position: absolute !important;
position: relative;
float: left;
top: 20%;
max-width: 25vw;
max-height: 80vh;
@@ -868,15 +863,9 @@
margin: 0;
}
&.backdropCard {
top: 35%;
}
&.squareCard {
top: 40%;
}
.layout-mobile & {
position: absolute;
top: auto;
left: 5%;
bottom: 1rem;
max-width: 30vw;
@@ -887,16 +876,16 @@
bottom: 0;
}
&,
&.backdropCard,
&.squareCard {
top: auto;
&.backdropCard {
top: 1.6em;
}
}
.layout-desktop & {
left: 3.3%;
top: -80%;
// ribbon height (7.2em) * 1.8
top: -12.96em;
margin-bottom: -12.96em; // match top offset
width: 25vw;
// FIXME: the fixed width + max height cause the card to be cropped this needs a proper fix
max-height: none;
@@ -904,9 +893,10 @@
.layout-tv & {
left: 5%;
top: 50%;
top: 0;
width: 25vw;
transform: translateY(-50%);
// match the width
margin-right: -25vw;
}
[dir="rtl"] & {
@@ -925,6 +915,28 @@
.detailPagePrimaryContent {
position: relative;
padding-top: 1.25em;
[dir="ltr"] & {
padding-left: 32.45vw;
padding-right: 2%;
}
[dir="rtl"] & {
padding-right: 32.45vw;
padding-left: 2%;
}
.layout-mobile & {
padding-left: 5% !important;
padding-right: 5% !important;
}
&::after {
content: "";
display: table;
clear: both;
}
}
.layout-mobile,

View File

@@ -252,6 +252,7 @@ a[data-role=button] {
background: none;
}
.noBackdropTransparency .detailPagePrimaryContainer,
.noBackdropTransparency .detailPageSecondaryContainer {
background-color: #d5e9f2;
}

View File

@@ -243,6 +243,7 @@ a[data-role=button] {
background: none;
}
.noBackdropTransparency .detailPagePrimaryContainer,
.noBackdropTransparency .detailPageSecondaryContainer {
background: url(bg.jpg) center top no-repeat #033361;
background-size: cover;

View File

@@ -225,6 +225,7 @@ html {
background: none;
}
.noBackdropTransparency .detailPagePrimaryContainer,
.noBackdropTransparency .detailPageSecondaryContainer {
background-color: #101010;
}

View File

@@ -257,6 +257,7 @@ a[data-role=button] {
color: inherit;
}
.noBackdropTransparency .detailPagePrimaryContainer,
.noBackdropTransparency .detailPageSecondaryContainer {
background-color: #f2f2f2;
}

View File

@@ -326,6 +326,7 @@ a[data-role=button] {
background: none;
}
.noBackdropTransparency .detailPagePrimaryContainer,
.noBackdropTransparency .detailPageSecondaryContainer {
background: url(bg.jpg) center top no-repeat #030322;
background-size: cover;

View File

@@ -228,6 +228,7 @@ a[data-role=button] {
background: none;
}
.noBackdropTransparency .detailPagePrimaryContainer,
.noBackdropTransparency .detailPageSecondaryContainer {
background: linear-gradient(to bottom, #0f3562, #1162a4, #03215f);
background-color: #0f3562;