304e8aa878
The top nav packed brand + health + pipeline chip + ~7 inline links + an action slot into one flex row, colliding/overflowing on phones (operator: 'almost unusable'). Below 768px the links now fold into a hamburger v-menu; below 480px the brand text hides (glyph still brands). Plus the primary browsing path: - BulkEditorPanel: fixed 320px -> min(320px, 90vw) so it can't swallow the screen. - GalleryFilterBar: <600px gives search its own full-width row (its 200px min-width was jamming the wrapping bar); sort grows. - GalleryFacetPanel: <480px wraps groups + lets the side-by-side date inputs grow full-width. - ArtistsView grid: minmax(min(440px,100%),1fr) so a card never overflows (single column on phones). - GalleryView: hide the year/month timeline strip <600px. ImageViewer already stacks its side panel below the image <900px (left as-is). Secondary surfaces (Posts/Subscriptions filter bars, SubscriptionsTab table, SeriesReader, PostCard) still need a mobile pass — follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
80 lines
2.6 KiB
Vue
80 lines
2.6 KiB
Vue
<template>
|
|
<v-container fluid class="pt-2 pb-6">
|
|
<Teleport to="#fc-nav-actions">
|
|
<v-btn
|
|
:color="sel.isSelectMode ? 'accent' : undefined"
|
|
:variant="sel.isSelectMode ? 'flat' : 'tonal'"
|
|
size="small"
|
|
@click="sel.isSelectMode ? sel.exitSelectMode() : sel.enterSelectMode()"
|
|
>{{ sel.isSelectMode ? 'Done' : 'Select' }}</v-btn>
|
|
</Teleport>
|
|
|
|
<div class="fc-gallery-layout">
|
|
<div class="fc-gallery-layout__main">
|
|
<PostInfoHeader />
|
|
<GalleryFilterBar v-if="store.filter.post_id == null" />
|
|
<EmptyState v-if="store.isEmpty" />
|
|
<GalleryGrid v-else @open="openImage" />
|
|
</div>
|
|
<TimelineSidebar
|
|
v-if="store.images.length > 0 && store.filter.similar_to == null"
|
|
class="fc-gallery-layout__sidebar"
|
|
/>
|
|
</div>
|
|
|
|
<BulkEditorPanel />
|
|
</v-container>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, watch } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useGalleryStore } from '../stores/gallery.js'
|
|
import { useModalStore } from '../stores/modal.js'
|
|
import GalleryGrid from '../components/gallery/GalleryGrid.vue'
|
|
import GalleryFilterBar from '../components/gallery/GalleryFilterBar.vue'
|
|
import TimelineSidebar from '../components/gallery/TimelineSidebar.vue'
|
|
import EmptyState from '../components/gallery/EmptyState.vue'
|
|
import PostInfoHeader from '../components/gallery/PostInfoHeader.vue'
|
|
import BulkEditorPanel from '../components/gallery/BulkEditorPanel.vue'
|
|
import { useGallerySelectionStore } from '../stores/gallerySelection.js'
|
|
|
|
const store = useGalleryStore()
|
|
const modal = useModalStore()
|
|
const sel = useGallerySelectionStore()
|
|
const route = useRoute()
|
|
|
|
// The URL query is the single source of truth for filters. Apply it on
|
|
// mount and on any query change (filter bar pushes, back button, deep-link).
|
|
onMounted(() => store.applyFilterFromQuery(route.query))
|
|
|
|
watch(() => route.query, (q) => {
|
|
sel.clear() // result set changed — selected ids are no longer valid
|
|
store.applyFilterFromQuery(q)
|
|
})
|
|
|
|
function openImage(id) {
|
|
modal.open(id)
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.fc-gallery-layout {
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: flex-start;
|
|
}
|
|
.fc-gallery-layout__main { flex: 1; min-width: 0; }
|
|
.fc-gallery-layout__sidebar { flex-shrink: 0; }
|
|
|
|
@media (max-width: 900px) {
|
|
.fc-gallery-layout { flex-direction: column-reverse; }
|
|
.fc-gallery-layout__sidebar { width: 100%; max-height: 200px; }
|
|
}
|
|
/* Phones: the year/month timeline strip eats vertical space and reads poorly
|
|
as a horizontal band — drop it; the gallery scroll is the primary nav here. */
|
|
@media (max-width: 600px) {
|
|
.fc-gallery-layout__sidebar { display: none; }
|
|
}
|
|
</style>
|