fix(series): sticky tabs + controls on the Series view
The Series tab strip and the Browse search/sort (and Suggestions controls) scrolled away on a long grid (operator-asked). Hoist the tabs + active-tab controls into one sticky header pinned under the 64px TopNav. The controls had to leave v-window — it clips sticky children — so they're driven by the tab from the header instead of living inside each window-item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,35 +1,60 @@
|
||||
<template>
|
||||
<v-container fluid class="pt-2 pb-6">
|
||||
<v-tabs v-model="tab" density="compact" class="mb-3">
|
||||
<v-tab value="browse">Browse</v-tab>
|
||||
<v-tab value="suggestions">
|
||||
Suggestions
|
||||
<v-badge
|
||||
v-if="sug.suggestions.length" :content="sug.suggestions.length"
|
||||
inline color="accent"
|
||||
<!-- Tabs + the active tab's controls live in one sticky block pinned under
|
||||
the 64px TopNav (operator-asked 2026-06-12), so the axis switcher and
|
||||
search/sort stay reachable on a long grid. The controls can't sit
|
||||
inside v-window (it clips sticky children), so they're hoisted here. -->
|
||||
<div class="fc-series__head">
|
||||
<v-tabs v-model="tab" density="compact">
|
||||
<v-tab value="browse">Browse</v-tab>
|
||||
<v-tab value="suggestions">
|
||||
Suggestions
|
||||
<v-badge
|
||||
v-if="sug.suggestions.length" :content="sug.suggestions.length"
|
||||
inline color="accent"
|
||||
/>
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
<div v-if="tab === 'browse'" class="fc-series-browse__controls">
|
||||
<v-text-field
|
||||
v-model="query"
|
||||
density="compact" variant="outlined" hide-details clearable
|
||||
label="Search series" prepend-inner-icon="mdi-magnify"
|
||||
style="max-width: 320px;"
|
||||
/>
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-select
|
||||
:model-value="store.sort"
|
||||
:items="sortItems"
|
||||
density="compact" variant="outlined" hide-details
|
||||
label="Sort" style="max-width: 220px;"
|
||||
@update:model-value="store.setSort($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else class="fc-sug__controls">
|
||||
<v-switch
|
||||
:model-value="sug.enabled" color="accent" density="compact"
|
||||
hide-details label="Matching on"
|
||||
@update:model-value="sug.setEnabled($event)"
|
||||
/>
|
||||
<v-text-field
|
||||
:model-value="sug.threshold"
|
||||
type="number" min="0" max="1" step="0.05"
|
||||
density="compact" variant="outlined" hide-details
|
||||
label="Threshold" style="max-width: 130px;"
|
||||
@change="sug.setThreshold(Number($event.target.value))"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="tonal" prepend-icon="mdi-radar" @click="sug.rescan()"
|
||||
>Rescan</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-window v-model="tab">
|
||||
<!-- ---- Browse ---- -->
|
||||
<v-window-item value="browse">
|
||||
<div class="fc-series-browse__controls">
|
||||
<v-text-field
|
||||
v-model="query"
|
||||
density="compact" variant="outlined" hide-details clearable
|
||||
label="Search series" prepend-inner-icon="mdi-magnify"
|
||||
style="max-width: 320px;"
|
||||
/>
|
||||
<v-select
|
||||
:model-value="store.sort"
|
||||
:items="sortItems"
|
||||
density="compact" variant="outlined" hide-details
|
||||
label="Sort" style="max-width: 220px;"
|
||||
@update:model-value="store.setSort($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<v-alert v-if="store.error" type="error" variant="tonal" closable class="my-4">
|
||||
Failed to load: {{ store.error }}
|
||||
</v-alert>
|
||||
@@ -138,25 +163,6 @@
|
||||
|
||||
<!-- ---- Suggestions ---- -->
|
||||
<v-window-item value="suggestions">
|
||||
<div class="fc-sug__controls">
|
||||
<v-switch
|
||||
:model-value="sug.enabled" color="accent" density="compact"
|
||||
hide-details label="Matching on"
|
||||
@update:model-value="sug.setEnabled($event)"
|
||||
/>
|
||||
<v-text-field
|
||||
:model-value="sug.threshold"
|
||||
type="number" min="0" max="1" step="0.05"
|
||||
density="compact" variant="outlined" hide-details
|
||||
label="Threshold" style="max-width: 130px;"
|
||||
@change="sug.setThreshold(Number($event.target.value))"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="tonal" prepend-icon="mdi-radar" @click="sug.rescan()"
|
||||
>Rescan</v-btn>
|
||||
</div>
|
||||
|
||||
<v-alert v-if="sug.error" type="error" variant="tonal" closable class="my-4">
|
||||
{{ sug.error }}
|
||||
</v-alert>
|
||||
@@ -287,8 +293,17 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Sticky header (tabs + active-tab controls) pinned under the 64px TopNav, so
|
||||
content scrolls cleanly beneath it. Surface bg matches SettingsView. */
|
||||
.fc-series__head {
|
||||
position: sticky;
|
||||
top: 64px;
|
||||
z-index: 4;
|
||||
background: rgb(var(--v-theme-surface));
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.fc-series-browse__controls {
|
||||
display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 16px;
|
||||
display: flex; flex-wrap: wrap; gap: 12px; padding-top: 12px;
|
||||
}
|
||||
.fc-series-browse__empty {
|
||||
padding: 48px; text-align: center;
|
||||
@@ -351,7 +366,7 @@ onMounted(() => {
|
||||
|
||||
/* Suggestions tab */
|
||||
.fc-sug__controls {
|
||||
display: flex; align-items: center; gap: 16px; margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 16px; padding-top: 12px;
|
||||
}
|
||||
.fc-sug__list { display: flex; flex-direction: column; gap: 8px; }
|
||||
.fc-sug__row {
|
||||
|
||||
Reference in New Issue
Block a user