Improved scenes and movies meta header layout and responsiveness.

This commit is contained in:
2026-11-12 00:57:30 +01:00
parent a8f6bcb4f0
commit 810b2971a8
6 changed files with 375 additions and 60 deletions
+81 -5
View File
@@ -15,6 +15,7 @@ import Filters from '#/components/filters/filters.vue';
import GroupsFilter from '#/components/filters/groups.vue';
import TagsFilter from '#/components/filters/tags.vue';
import FilterToggle from '#/components/filters/toggle.vue';
import YearsFilter from '#/components/filters/years.vue';
import Ellipsis from '#/components/loading/ellipsis.vue';
import Pagination from '#/components/pagination/pagination.vue';
@@ -74,6 +75,11 @@ const total = ref(Number(pageProps.sceneTotal || pageProps.total));
const loading = ref(false);
const scenesContainer = ref(null);
// desktop defaults to open, compact defaults to closed
// we can't measure the viewport in SSR, so use separate toggles, each viewport only reads its own
const showFiltersFull = ref(true);
const showFiltersCompact = ref(false);
const view = ref(env.scenesView || 'grid');
const actorIds = urlParsed.search.actors?.split(',').map((identifier) => parseActorIdentifier(identifier)?.id).filter(Boolean) || [];
@@ -97,6 +103,20 @@ const filters = ref({
trans: parseRange(urlParsed.search.trans),
});
// the search input updates the filter while typing, only count the search once it's applied
const appliedSearch = ref(urlParsed.search.q);
// indicates the results are narrowed down: the search, each tag, actor and the channel count individually, each range counts once
// the page's own actor, tag or channel aren't in these filters, so they aren't counted
const filterCount = computed(() => [
appliedSearch.value?.trim() ? 1 : 0,
filters.value.tags.length,
filters.value.actors.length,
filters.value.entity ? 1 : 0,
filters.value.years.length > 0 || filters.value.yearRange ? 1 : 0,
...['group', 'females', 'males', 'trans'].map((key) => (filters.value[key] ? 1 : 0)),
].reduce((acc, count) => acc + count, 0));
const sceneCampaign = campaigns?.scenes;
const campaignIndex = campaigns?.index;
@@ -123,6 +143,8 @@ function getPath(targetScope, preserveQuery) {
}
async function search(options = {}) {
appliedSearch.value = filters.value.search;
if (options.resetPage !== false) {
currentPage.value = 1;
}
@@ -196,6 +218,11 @@ function updateFilter(prop, value, reload = true) {
}
}
function toggleFilters(state) {
showFiltersFull.value = state ?? !showFiltersFull.value;
showFiltersCompact.value = state ?? !showFiltersCompact.value;
}
function setView(newView) {
view.value = newView;
Cookies.set('scenesView', newView);
@@ -211,6 +238,9 @@ function setView(newView) {
<Filters
v-if="showFilters"
:loading="loading"
:show-full="showFiltersFull"
:show-compact="showFiltersCompact"
@close="toggleFilters(false)"
>
<div class="filter search-container">
<!-- onsearch not compatible with FF and Safari -->
@@ -274,16 +304,34 @@ function setView(newView) {
class="scenes-container"
:class="{ loading }"
>
<Campaign
v-if="campaigns?.meta"
:campaign="campaigns.meta"
class="campaign-meta campaign-full"
/>
<div
v-if="showMeta"
class="scenes-header"
>
<div class="meta">{{ total }} results</div>
<span class="header-filter">
<FilterToggle
v-if="showFilters"
:show-full="showFiltersFull"
:show-compact="showFiltersCompact"
:count="filterCount"
@toggle="toggleFilters()"
/>
<div
class="meta"
>{{ total }}<span class="meta-label">&nbsp;results</span></div>
</span>
<Campaign
v-if="campaigns?.meta"
:campaign="campaigns.meta"
class="campaign-meta"
class="campaign-meta campaign-gap"
/>
<div class="views">
@@ -430,12 +478,12 @@ function setView(newView) {
display: flex;
align-items: flex-end;
justify-content: space-between;
padding: .5rem 1rem .25rem 4rem;
padding: .5rem 1rem .5rem 0;
.campaign {
max-height: 6rem;
justify-content: center;
margin: .5rem 1rem 0 1rem;
margin: 0 1rem;
/* makes empty area link to ad, bit too annoying
width: 0;
flex-grow: 1;
@@ -455,7 +503,13 @@ function setView(newView) {
justify-content: space-between;
align-items: center;
flex-shrink: 0;
margin-bottom: .5rem;
margin-top: 2px;
}
.header-filter {
display: flex;
align-items: center;
flex-shrink: 0;
}
.scenes-items {
@@ -476,6 +530,14 @@ function setView(newView) {
width: auto;
}
.campaign-full {
height: auto;
}
.campaign-full {
display: none;
}
.campaign-scene {
width: 100%;
aspect-ratio: 2 / 1; /* ensures banner can't be taller than scene tile */
@@ -579,6 +641,14 @@ function setView(newView) {
.scopes .views {
width: 100%;
}
.campaign-gap {
display: none;
}
.campaign-full {
display: flex;
}
}
@media(--small-20) {
@@ -600,4 +670,10 @@ function setView(newView) {
justify-content: flex-start;
}
}
@media(--small-50) {
.meta-label {
display: none;
}
}
</style>