This commit is contained in:
team3
2026-09-18 22:12:01 +02:00
parent 33b3975b61
commit db77c1f985
10 changed files with 488 additions and 298 deletions

View File

@@ -21,7 +21,7 @@ import { matches, useKatalog } from './composables/useKatalog'
import { REGLER, useFilter } from './composables/useFilter'
import { useLangeListe } from './composables/useLangeListe'
const { ansicht, ohneGesehene, activeTab, host } = useEinstellungen()
const { ansicht, ohneGesehene, activeTab, quelle, suchArt, host } = useEinstellungen()
const {
runde, ich, michSetzen,
@@ -44,7 +44,7 @@ const {
const {
search, sort, suchSort, include, exclude, grenzen, ranges, genreOptions,
toggleGenre, toggleAll, resetGenres, nichtGesehen, passt, suchFilter,
} = useFilter({ activeTab, netflixMovies, canonMovies, gesehenIds, ohneGesehene })
} = useFilter({ activeTab, quelle, netflixMovies, canonMovies, gesehenIds, ohneGesehene })
// Eine Reihe erscheint, sobald ein Teil passt — sortiert wird sie über
// ihren stärksten Teil
@@ -69,24 +69,31 @@ const visibleCanon = computed(() =>
)
const aktuelleListe = computed(() =>
activeTab.value === 'netflix' ? visible.value : visibleCanon.value
quelle.value === 'netflix' ? visible.value : visibleCanon.value
)
// Die zwei Listen des Katalogs, umschaltbar in der Filterleiste
const quellen = computed(() => [
{ id: 'netflix', label: 'Netflix', count: netflixMovies.value.filter(nichtGesehen).length },
{
id: 'canon',
label: 'Horror-Kanon',
count: canonGroups.value.filter((g) => g.parts.some(nichtGesehen)).length,
},
])
const { gezeigt, sichtbar, nachschub, obenKnopf, nachOben } = useLangeListe({
liste: aktuelleListe,
ansicht,
})
const tabs = computed(() => [
{ id: 'netflix', label: 'Netflix', count: netflixMovies.value.filter(nichtGesehen).length },
{
id: 'canon',
label: 'Horror-Kanon',
count: canonGroups.value.filter((g) => g.parts.some(nichtGesehen)).length,
id: 'katalog',
label: 'Katalog',
count: quellen.value.find((q) => q.id === quelle.value)?.count ?? null,
},
{ id: 'search', label: 'Suche', count: null },
{ id: 'aehnlich', label: 'Ähnlichkeitsuche', count: null },
{ id: 'ki', label: 'KI-Suche', count: null },
{ id: 'suche', label: 'Suche', count: null },
...(host.value ? [] : [{ id: 'merkliste', label: 'Sammlungen', count: null }]),
{ id: 'wheel', label: 'Rad', count: null },
{ id: 'stream', label: 'Stream', count: null },
@@ -101,7 +108,7 @@ const tabs = computed(() => [
// legt sie sich über einen Knopf über die Seite
const filterOffen = ref(false)
const mitFilter = computed(() =>
['netflix', 'canon', 'search', 'aehnlich', 'ki', 'merkliste'].includes(activeTab.value)
['katalog', 'suche', 'merkliste'].includes(activeTab.value)
)
// Der Sammlungen-Reiter filtert nach Person und Genre, nicht nach Regler —
@@ -125,7 +132,7 @@ const leuteMitSammlung = computed(() => {
// Fällt der offene Reiter weg — Hostmodus aus, Name abgewählt —, wäre
// sonst keiner markiert und die Seite bliebe leer
watch(tabs, (liste) => {
if (!liste.some((t) => t.id === activeTab.value)) activeTab.value = 'netflix'
if (!liste.some((t) => t.id === activeTab.value)) activeTab.value = 'katalog'
})
// Passen die Reiter nicht in eine Zeile, soll der offene sichtbar sein
@@ -184,9 +191,17 @@ const aehnlichStart = ref(null)
function zurAehnlichkeitsuche(movie) {
aehnlichStart.value = movie
selected.value = null
activeTab.value = 'aehnlich'
activeTab.value = 'suche'
suchArt.value = 'aehnlich'
}
// Die drei Suchen unter einem Reiter, umschaltbar über der Liste
const SUCHARTEN = [
{ id: 'search', label: 'Titel' },
{ id: 'aehnlich', label: 'Ähnliche Filme' },
{ id: 'ki', label: 'KI' },
]
const rad = ref(null)
function nachVorschlag() {
@@ -232,26 +247,51 @@ onMounted(load)
</div>
</header>
<div v-if="activeTab === 'search' || activeTab === 'aehnlich'" class="layout">
<SearchTab
v-if="activeTab === 'search'"
:gesehen-ids="gesehenIds"
:ohne-gesehene="ohneGesehene"
:filter="suchFilter"
:ansicht="ansicht"
@open="selected = $event"
/>
<div v-if="activeTab === 'suche'" class="layout">
<div class="suche">
<div class="sucharten">
<button
v-for="art in SUCHARTEN"
:key="art.id"
type="button"
:class="{ active: suchArt === art.id }"
@click="suchArt = art.id"
>
{{ art.label }}
</button>
</div>
<AehnlichTab
v-else
:gesehen-ids="gesehenIds"
:filter="suchFilter"
:ansicht="ansicht"
:start="aehnlichStart"
@open="filmOeffnen"
@gestartet="aehnlichStart = null"
/>
<SearchTab
v-if="suchArt === 'search'"
:gesehen-ids="gesehenIds"
:ohne-gesehene="ohneGesehene"
:filter="suchFilter"
:ansicht="ansicht"
@open="selected = $event"
/>
<AehnlichTab
v-else-if="suchArt === 'aehnlich'"
:gesehen-ids="gesehenIds"
:filter="suchFilter"
:ansicht="ansicht"
:start="aehnlichStart"
@open="filmOeffnen"
@gestartet="aehnlichStart = null"
/>
<KiTab
v-else
:angemeldet="angemeldet"
:gesehen-ids="gesehenIds"
:ohne-gesehene="ohneGesehene"
:filter="suchFilter"
:ansicht="ansicht"
@open="selected = $event"
/>
</div>
<!-- Die KI ordnet selbst nach Passung, da gibt es nichts zu sortieren -->
<FilterSidebar
v-model:sort="suchSort"
v-model:ansicht="ansicht"
@@ -264,35 +304,8 @@ onMounted(load)
:include="include"
:exclude="exclude"
:ohne-suche="true"
:mit-relevanz="true"
@toggle="toggleGenre"
@all="toggleAll"
@reset="resetGenres"
/>
</div>
<div v-else-if="activeTab === 'ki'" class="layout">
<KiTab
:angemeldet="angemeldet"
:gesehen-ids="gesehenIds"
:ohne-gesehene="ohneGesehene"
:filter="suchFilter"
:ansicht="ansicht"
@open="selected = $event"
/>
<FilterSidebar
v-model:ansicht="ansicht"
v-model:ohne-gesehene="ohneGesehene"
v-model:ranges="ranges"
:class="{ offen: filterOffen }"
:regler="REGLER"
:grenzen="grenzen"
:options="genreOptions"
:include="include"
:exclude="exclude"
:ohne-suche="true"
:ohne-sort="true"
:mit-relevanz="suchArt !== 'ki'"
:ohne-sort="suchArt === 'ki'"
@toggle="toggleGenre"
@all="toggleAll"
@reset="resetGenres"
@@ -399,6 +412,7 @@ onMounted(load)
<FilterSidebar
v-if="genreOptions.length"
v-model:quelle="quelle"
v-model:search="search"
v-model:sort="sort"
v-model:ansicht="ansicht"
@@ -410,6 +424,7 @@ onMounted(load)
:options="genreOptions"
:include="include"
:exclude="exclude"
:quellen="quellen"
@toggle="toggleGenre"
@all="toggleAll"
@reset="resetGenres"
@@ -570,6 +585,39 @@ onMounted(load)
min-width: 0;
}
.suche {
min-width: 0;
}
/* Umschalter zwischen den drei Suchen: drei gleich breite Knöpfe über
die volle Breite, im Stil von Kompakt/Details in der Leiste */
.sucharten {
display: flex;
gap: 0.4rem;
margin-bottom: 1.2rem;
}
.sucharten button {
flex: 1;
padding: 0.6rem 0.9rem;
color: var(--muted);
font-size: 0.9rem;
white-space: nowrap;
background: var(--bg-soft);
border: 1px solid var(--line);
border-radius: 8px;
}
.sucharten button:hover {
color: var(--text);
}
.sucharten button.active {
color: var(--text);
font-weight: 600;
border-color: var(--accent);
}
.results {
min-width: 0;
}