Update works page with new layout; fix audio player to only autoplay on click

This commit is contained in:
Michael Winter 2026-03-07 13:16:46 +01:00
parent be041de733
commit 69fab67601
4 changed files with 102 additions and 37 deletions

View file

@ -22,7 +22,6 @@
height="20px" height="20px"
scrolling="no" scrolling="no"
frameborder="no" frameborder="no"
allow="autoplay"
:src="'https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/' + audioPlayerStore.soundcloud_trackid + '&inverse=false&auto_play=true&show_user=false'" :src="'https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/' + audioPlayerStore.soundcloud_trackid + '&inverse=false&auto_play=true&show_user=false'"
></iframe> ></iframe>
</ClientOnly> </ClientOnly>

View file

@ -20,10 +20,10 @@
</span> </span>
<div class="flex gap-1"> <div class="flex gap-1">
<IconButton v-if="work.score" type="score" :work="work" :link="work.score"></IconButton> <IconButton v-if="work.score" type="score" :work="work" :link="work.score" :visible="true"></IconButton>
<IconButton v-if="work.soundcloud_trackid" type="audio" :work="work"></IconButton> <IconButton v-if="work.soundcloud_trackid" type="audio" :work="work" :visible="true"></IconButton>
<IconButton v-if="work.vimeo_trackid" type="video" :work="work"></IconButton> <IconButton v-if="work.vimeo_trackid" type="video" :work="work" :visible="true"></IconButton>
<IconButton v-if="work.gallery" type="image" :work="work"></IconButton> <IconButton v-if="work.gallery" type="image" :work="work" :visible="true"></IconButton>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,29 +1,81 @@
<template> <template>
<div class="bg-zinc-100 rounded-lg mx-5 mt-5 p-5"> <div>
<div v-if="work"> <div v-if="work">
<div class="sticky top-[100px] z-10 bg-zinc-100 pb-4 pt-2"> <div class="sticky top-[64px] bg-white z-30 border-b border-gray-200">
<div class="max-w-[800px] mx-auto"> <div class="max-w-3xl mx-auto px-4">
<h1 class="text-4xl italic text-center mb-4" v-html="work.title"></h1> <div class="flex items-center justify-between py-1">
<div class="flex items-baseline gap-1">
<nav class="flex gap-4 mb-6"> <h1 class="text-xl italic" v-html="work.title"></h1>
<a v-if="work.vimeo_trackid" href="#video" class="hover:underline">Video</a> <span class="text-sm text-gray-500">({{ year }})</span>
<a v-if="gallery && gallery.length" href="#images" class="hover:underline">Images</a> </div>
<a v-if="scoreUrl" href="#score" class="hover:underline">Score</a> <nav class="flex items-center gap-4 text-sm">
</nav> <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> </div>
<div class="max-w-[800px] mx-auto"> <div class="max-w-3xl mx-auto space-y-8 px-4 pt-6">
<div id="video" v-if="work.vimeo_trackid" class="mb-8 scroll-mt-[280px]"> <div v-if="work.soundcloud_trackid" id="audio">
<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> <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"
></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%"
height="400"
frameborder="0"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen
class="w-full aspect-video"
></iframe>
</div> </div>
<div id="images" v-if="gallery && gallery.length" class="mb-8 scroll-mt-[280px]"> <div v-if="gallery?.length" id="images">
<ImageSlider :bucket="'images'" :gallery="gallery"></ImageSlider> <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>
<div id="score" v-if="scoreUrl" class="mb-8 scroll-mt-[280px]"> <div v-if="scoreUrl" id="score">
<iframe :src="scoreUrl + '#toolbar=1&navpanes=0&sidebar=0'" class="w-full h-[85vh] border"></iframe> <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>
</div> </div>
@ -34,10 +86,10 @@
</template> </template>
<script setup> <script setup>
import { useAudioPlayerStore } from "@/stores/AudioPlayerStore" import { useModalStore } from "@/stores/ModalStore"
const route = useRoute() const route = useRoute()
const audioPlayerStore = useAudioPlayerStore() const modalStore = useModalStore()
const slug = route.params.slug const slug = route.params.slug
const slugify = (title) => { const slugify = (title) => {
@ -54,6 +106,11 @@ const work = computed(() => {
return works.value.find(w => slugify(w.title) === slug) 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(() => { const scoreUrl = computed(() => {
if (!work.value?.score) return null if (!work.value?.score) return null
if (work.value.score.startsWith('/scores/')) { if (work.value.score.startsWith('/scores/')) {
@ -69,23 +126,27 @@ const gallery = computed(() => {
})) }))
}) })
const itemCount = computed(() => { const navItems = computed(() => {
let count = 0 const items = []
if (work.value?.vimeo_trackid) count++ if (work.value?.soundcloud_trackid) items.push({ label: 'Audio', href: '#audio' })
if (gallery.value?.length) count++ if (work.value?.vimeo_trackid) items.push({ label: 'Video', href: '#video' })
if (scoreUrl.value) count++ if (gallery.value?.length) items.push({ label: 'Images', href: '#images' })
return count if (scoreUrl.value) items.push({ label: 'Score', href: '#score' })
return items
}) })
onMounted(() => { const openImageModal = (index) => {
if (work.value?.soundcloud_trackid) { modalStore.setModalProps('image', 'aspect-auto', true, 'images', gallery.value, '')
audioPlayerStore.setSoundCloudTrackID(work.value.soundcloud_trackid) }
}
useHead({
titleTemplate: () => work.value ? `Michael Winter - ${work.value.title}` : 'Michael Winter'
}) })
</script> </script>
<style> <style>
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
scroll-padding-top: 100px;
} }
</style> </style>

View file

@ -1,15 +1,20 @@
import {defineStore} from "pinia"; import {defineStore} from "pinia";
export const useAudioPlayerStore = defineStore("AudioPlayerStore", { export const useAudioPlayerStore = defineStore("AudioPlayerStore", {
state: () => ({"soundcloud_trackid": "1032587794"}), state: () => ({
"soundcloud_trackid": "undefined",
"currentTrackTitle": ""
}),
actions: { actions: {
setSoundCloudTrackID(trackid) { setSoundCloudTrackID(trackid, title = "") {
if (typeof trackid !== 'undefined') { if (typeof trackid !== 'undefined') {
this.soundcloud_trackid = trackid this.soundcloud_trackid = trackid
this.currentTrackTitle = title
} }
}, },
clearSoundCloudTrackID() { clearSoundCloudTrackID() {
this.soundcloud_trackid = 'undefined' this.soundcloud_trackid = 'undefined'
this.currentTrackTitle = ""
} }
} }
}) })