portfolio/pages/works/[slug].vue
Michael Winter 6e9f859d59 Make mobile and desktop behave identically
- Remove isMobile() function from IconButton and works page
- Remove mobile-specific conditionals for score and document icons
- Always show nav and score iframe on works page
- No more hydration mismatch - same HTML on server and client
2026-03-06 18:53:35 +01:00

92 lines
2.7 KiB
Vue

<template>
<div class="bg-zinc-100 rounded-lg mx-5 mt-5 p-5">
<div v-if="work">
<div class="sticky top-[100px] z-10 bg-zinc-100 pb-4 pt-2">
<div class="max-w-[800px] mx-auto">
<h1 class="text-4xl italic text-center mb-4" v-html="work.title"></h1>
<nav class="flex gap-4 mb-6">
<a v-if="work.vimeo_trackid" href="#video" class="hover:underline">Video</a>
<a v-if="gallery && gallery.length" href="#images" class="hover:underline">Images</a>
<a v-if="scoreUrl" href="#score" class="hover:underline">Score</a>
</nav>
</div>
</div>
<div class="max-w-[800px] mx-auto">
<div id="video" v-if="work.vimeo_trackid" class="mb-8 scroll-mt-[280px]">
<iframe :src="'https://player.vimeo.com/video/' + work.vimeo_trackid" width="100%" height="100%" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen class="w-full aspect-video"></iframe>
</div>
<div id="images" v-if="gallery && gallery.length" class="mb-8 scroll-mt-[280px]">
<ImageSlider :bucket="'images'" :gallery="gallery"></ImageSlider>
</div>
<div id="score" v-if="scoreUrl" class="mb-8 scroll-mt-[280px]">
<iframe :src="scoreUrl + '#toolbar=1&navpanes=0&sidebar=0'" class="w-full h-[85vh] border"></iframe>
</div>
</div>
</div>
<div v-else class="text-center p-10">
<p>Work not found</p>
</div>
</div>
</template>
<script setup>
import { useAudioPlayerStore } from "@/stores/AudioPlayerStore"
const route = useRoute()
const audioPlayerStore = useAudioPlayerStore()
const slug = route.params.slug
const slugify = (title) => {
if (!title) return ''
return title.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '')
}
const { data: works } = await useFetch('/api/works', {
key: 'works-workpage-' + slug,
})
const work = computed(() => {
if (!works.value) return null
return works.value.find(w => slugify(w.title) === slug)
})
const scoreUrl = computed(() => {
if (!work.value?.score) return null
if (work.value.score.startsWith('/scores/')) {
return work.value.score
}
return '/scores/' + work.value.score
})
const gallery = computed(() => {
if (!work.value?.images) return null
return work.value.images.map(img => ({
image: img.filename
}))
})
const itemCount = computed(() => {
let count = 0
if (work.value?.vimeo_trackid) count++
if (gallery.value?.length) count++
if (scoreUrl.value) count++
return count
})
onMounted(() => {
if (work.value?.soundcloud_trackid) {
audioPlayerStore.setSoundCloudTrackID(work.value.soundcloud_trackid)
}
})
</script>
<style>
html {
scroll-behavior: smooth;
}
</style>