portfolio/pages/works/[slug].vue

153 lines
4.9 KiB
Vue
Raw Normal View History

<template>
<div>
<div v-if="work">
<div class="sticky top-12 bg-white z-30 border-b border-gray-200">
<div class="max-w-3xl mx-auto px-4">
<div class="flex items-center justify-between py-1">
2026-03-07 17:39:14 +01:00
<div class="flex flex-wrap items-baseline gap-1">
<h1 class="inline-block text-xl italic" v-html="work.title"></h1>
<span class="inline-block text-sm text-gray-500">({{ year }})</span>
</div>
<nav class="flex items-center gap-4 text-sm">
<a
v-for="item in navItems"
:key="item.href"
:href="item.href"
class="hover:underline"
>
{{ item.label }}
</a>
<NuxtLink to="/pieces" class="text-gray-500 hover:text-gray-700">
<Icon name="mdi:arrow-u-left-top" class="w-4 h-4 translate-y-0.5" />
</NuxtLink>
</nav>
</div>
</div>
</div>
<div class="max-w-3xl mx-auto space-y-8 px-4 pt-6">
<div v-if="work.soundcloud_trackid" id="audio">
<h2 class="text-lg mb-4 border-b border-gray-200 pb-2">Audio</h2>
<iframe
:src="'https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/' + work.soundcloud_trackid + '&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true'"
width="100%"
height="150"
scrolling="no"
frameborder="no"
allow="autoplay"
2026-03-07 17:39:14 +01:00
class="w-full h-auto"
></iframe>
</div>
<div v-if="work.vimeo_trackid" id="video">
<h2 class="text-lg mb-4 border-b border-gray-200 pb-2">Video</h2>
<iframe
:src="'https://player.vimeo.com/video/' + work.vimeo_trackid"
width="100%"
frameborder="0"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen
2026-03-07 17:39:14 +01:00
class="w-full aspect-video h-auto"
></iframe>
</div>
<div v-if="gallery?.length" id="images">
<h2 class="text-lg mb-4 border-b border-gray-200 pb-2">Images</h2>
<div :class="gallery.length === 1 ? 'grid grid-cols-1' : 'grid grid-cols-2 gap-4'">
<button
v-for="(img, index) in gallery"
:key="index"
@click="openImageModal(index)"
class="block w-full relative overflow-hidden group"
>
<nuxt-img
:src="'/images/' + img.image"
:alt="work.title"
class="w-full aspect-[4/3] object-cover transition-opacity duration-200 group-hover:opacity-50"
/>
</button>
</div>
</div>
<div v-if="scoreUrl" id="score">
<h2 class="text-lg mb-4 border-b border-gray-200 pb-2">Score</h2>
<iframe
:src="scoreUrl + '#toolbar=1&navpanes=0&sidebar=0'"
class="w-full h-[80vh] border border-gray-200"
></iframe>
</div>
</div>
</div>
<div v-else class="text-center p-10">
<p>Work not found</p>
</div>
</div>
</template>
<script setup>
import { useModalStore } from "@/stores/ModalStore"
const route = useRoute()
const modalStore = useModalStore()
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 year = computed(() => {
if (!work.value?.date) return ''
return new Date(work.value.date).getFullYear()
})
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 navItems = computed(() => {
const items = []
if (work.value?.soundcloud_trackid) items.push({ label: 'Audio', href: '#audio' })
if (work.value?.vimeo_trackid) items.push({ label: 'Video', href: '#video' })
if (gallery.value?.length) items.push({ label: 'Images', href: '#images' })
if (scoreUrl.value) items.push({ label: 'Score', href: '#score' })
return items
})
const openImageModal = (index) => {
modalStore.setModalProps('image', 'aspect-auto', true, 'images', gallery.value, '')
}
useHead({
titleTemplate: () => work.value ? `Michael Winter - ${work.value.title}` : 'Michael Winter'
})
</script>
<style>
html {
scroll-behavior: smooth;
scroll-padding-top: 100px;
}
</style>