- Score icon: mobile downloads PDF, desktop opens modal - Document icon: mobile downloads PDF, desktop opens modal - Works page: mobile hides score iframe, Score link downloads PDF - Uses pure CSS (md:hidden/hidden md:block) - no JS needed
97 lines
3 KiB
Vue
97 lines
3 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 v-if="itemCount >= 2" 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>
|
|
<span class="md:hidden">
|
|
<a v-if="scoreUrl" :href="scoreUrl" class="hover:underline">Score</a>
|
|
</span>
|
|
<span class="hidden md:block">
|
|
<a v-if="scoreUrl" href="#score" class="hover:underline">Score</a>
|
|
</span>
|
|
</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] hidden md:block">
|
|
<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>
|