add cards & tracks
All checks were successful
Deploy App / build (push) Successful in 1m13s
Deploy App / deploy (push) Successful in 15s

This commit is contained in:
valere
2025-10-02 00:38:54 +02:00
parent 8c1290beae
commit 43b1a11027
11 changed files with 474 additions and 43 deletions

View File

@@ -130,7 +130,7 @@ onMounted(() => {
const down = (ev: PointerEvent) => {
ev.preventDefault()
dragging = true
scene.value?.setPointerCapture(ev.pointerId)
box.value?.setPointerCapture(ev.pointerId)
lastPointer = { x: ev.clientX, y: ev.clientY, time: performance.now() }
velocity = { x: 0, y: 0 }
if (raf) { cancelAnimationFrame(raf); raf = null }
@@ -158,17 +158,17 @@ onMounted(() => {
const end = (ev: PointerEvent) => {
if (!dragging) return
dragging = false
try { scene.value?.releasePointerCapture(ev.pointerId) } catch { }
try { box.value?.releasePointerCapture(ev.pointerId) } catch { }
if (enableInertia && (Math.abs(velocity.x) > minVelocity || Math.abs(velocity.y) > minVelocity)) {
if (!raf) raf = requestAnimationFrame(tickInertia)
}
}
scene.value?.addEventListener('pointerdown', down)
scene.value?.addEventListener('pointermove', move)
scene.value?.addEventListener('pointerup', end)
scene.value?.addEventListener('pointercancel', end)
scene.value?.addEventListener('pointerleave', end)
box.value?.addEventListener('pointerdown', down)
box.value?.addEventListener('pointermove', move)
box.value?.addEventListener('pointerup', end)
box.value?.addEventListener('pointercancel', end)
box.value?.addEventListener('pointerleave', end)
onBeforeUnmount(() => {
cancelAnimationFrame(raf!)

View File

@@ -1,14 +1,17 @@
<template>
<article class="relative">
<main
class="absolute top-0 backdrop-blur-sm z-40 -mt-12 z-10 card w-56 h-80 p-3 bg-opacity-10 bg-white rounded-2xl shadow-lg flex flex-col overflow-hidden">
class="backdrop-blur-sm border-2 -mt-12 z-10 card w-56 h-80 p-3 bg-opacity-40 hover:bg-opacity-80 hover:shadow-xl transition-all bg-white rounded-2xl shadow-lg flex flex-col overflow-hidden">
<!-- Cover -->
<figure class="flex-1 overflow-hidden rounded-t-xl">
<figure @click="playerStore.playTrack(props.track)" class="flex-1 overflow-hidden rounded-t-xl cursor-pointer">
<img :src="coverUrl" alt="Pochette de l'album" class="w-full h-full object-cover object-center" />
</figure>
<!-- Body -->
<div class="p-3 text-center bg-white rounded-b-xl">
<div class="label">
{{ props.track.order }}
</div>
<h2 class="text-base text-neutral-800 font-bold truncate">{{ props.track.title }}</h2>
<p class="text-sm text-neutral-500 truncate">
{{ props.track.artist.name }}
@@ -17,11 +20,14 @@
</main>
<footer
class="absolute top-0 ml-32 backdrop-blur-sm -mt-12 z-10 card w-56 h-80 p-3 bg-opacity-10 bg-white rounded-2xl shadow-lg flex flex-col overflow-hidden">
class="ml-32 backdrop-blur-sm -mt-12 z-10 card w-56 h-80 p-3 bg-opacity-10 bg-white rounded-2xl shadow-lg flex flex-col overflow-hidden">
<!-- Back -->
<div class="h-full flex p-16 text-center bg-slate-800 rounded-xl">
<img src="/favicon.svg" />
</div>
<div class="label">
{{ props.track.id }}
</div>
</footer>
</article>
@@ -29,7 +35,19 @@
<script setup lang="ts">
import type { Track } from '~~/types/types'
import { usePlayerStore } from '~/store/player'
const props = defineProps<{ track: Track }>()
const coverUrl = `https://f4.bcbits.com/img/${props.track.artist.coverId}_4.jpg`
</script>
const playerStore = usePlayerStore()
const coverUrl = props.track.coverId.startsWith('http')
? props.track.coverId
: `https://f4.bcbits.com/img/${props.track.coverId}_4.jpg`;
</script>
<style>
.label {
@apply rounded-full size-7 p-2 bg-esyellow leading-3 -mt-6;
font-weight: bold;
text-align: center;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<audio ref="audioRef" class="fixed z-50 bottom-0 left-1/2 -translate-x-1/2 w-1/2"
:src="playerStore.currentTrack ? playerStore.getCompilationUrlFromTrack(playerStore.currentTrack) : ''" controls
@timeupdate="updatePosition" @ended="onEnded" />
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { usePlayerStore } from '~/store/player'
const playerStore = usePlayerStore()
const audioRef = ref<HTMLAudioElement | null>(null)
onMounted(() => {
if (audioRef.value) playerStore.audio = audioRef.value
})
// Mettre à jour la position
function updatePosition() {
if (audioRef.value) playerStore.position = audioRef.value.currentTime
}
function onEnded() {
playerStore.isPlaying = false
}
// Si la track change, mettre à jour le src et le start
watch(
() => playerStore.currentTrack,
(newTrack) => {
if (newTrack && audioRef.value) {
audioRef.value.src = newTrack.url
audioRef.value.currentTime = newTrack.start || 0
if (playerStore.isPlaying) audioRef.value.play()
}
}
)
</script>

View File

@@ -12,7 +12,6 @@ import type { BoxState } from '~~/types/types'
import { useRouter } from 'vue-router'
const dataStore = useDataStore()
const router = useRouter()
const boxStates = ref<Record<string, BoxState>>({})
function openCompilation(id: string) {
@@ -20,7 +19,6 @@ function openCompilation(id: string) {
for (const key in boxStates.value) {
boxStates.value[key] = (key === id) ? 'selected' : 'hide'
}
window.history.pushState({}, '', '/compilation/' + id)
}
}
@@ -30,7 +28,6 @@ function closeCompilation(e: KeyboardEvent) {
boxStates.value[key] = 'list'
}
}
window.history.pushState({}, '', '/')
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="mt-8 p-8 w-96">
<div class="mt-8 p-8 w-96 flex flex-wrap">
<MoleculeCard v-for="track in dataStore.getTracksByCompilationId(compilation.id)" :key="track.id" :track="track" />
</div>
</template>
@@ -13,5 +13,4 @@ const props = defineProps<{
}>()
const dataStore = useDataStore()
</script>