bucket cards management
All checks were successful
Deploy App / build (push) Successful in 3m57s
Deploy App / deploy (push) Successful in 17s

This commit is contained in:
valere
2025-12-31 17:23:11 +01:00
parent 9001025837
commit a5fe876e3f
4 changed files with 94 additions and 38 deletions

View File

@@ -9,7 +9,7 @@
</div>
<div class="face back flex flex-row flex-wrap items-start p-4 overflow-hidden"
:class="{ 'overflow-y-scroll': !isCompilation }" ref="backFace">
<li class="list-none text-xxs w-1/2 flex flex-row"
<!-- <li class="list-none text-xxs w-1/2 flex flex-row"
v-for="track in dataStore.getTracksByboxId(box.id, box.activeSide)" :key="track.id" :track="track">
<span class="text-slate-700" v-if="isNotManifesto"> {{ track.order }}. </span>
<p class="text-left text-slate-700">
@@ -19,7 +19,7 @@
<br />
{{ track.artist.name }}
</p>
</li>
</li> -->
</div>
<div class="face right" ref="rightFace" />
<div class="face left" ref="leftFace" />

View File

@@ -60,6 +60,8 @@ onMounted(() => {
// Gestion du drag and drop desktop
const handleDragStart = (event: { item: HTMLElement }) => {
drag.value = true
// Émettre un événement personnalisé pour indiquer qu'un glisser a commencé depuis le bucket
document.dispatchEvent(new CustomEvent('bucket-drag-start'))
}
const handleDragEnd = (event: { item: HTMLElement; newIndex: number; oldIndex: number }) => {

View File

@@ -6,6 +6,8 @@
<div
class="bobine bg-slate-900 bg-opacity-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full"
:style="{ height: platineStore.progressPercentage + '%', width: platineStore.progressPercentage + '%' }"></div>
<img class="absolute size-full rounded-full" :src="platineStore.currentTrack?.coverId"
:alt="platineStore.currentTrack?.title">
<div class="disc-label rounded-full bg-cover bg-center">
<img src="/favicon.svg" class="size-1/3">
@@ -13,14 +15,14 @@
<div class="spinner"></div>
</div>
</div>
<div class="w-full h-1/5 flex justify-center items-center text-8xl text-white absolute pointer-events-none">
{{ platineStore.currentTrack?.title }}
<br>
{{ platineStore.currentTrack?.artist.name }}
</div>
<div v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
</div>
</div>
<div class="w-full h-1/5 text-base">
{{ platineStore.currentTrack?.title }}
<br>
{{ platineStore.currentTrack?.artist?.name }}
</div>
</div>
</template>

View File

@@ -10,7 +10,8 @@
<SelectCardRank @change="onRankChange" />
<SelectCardSuit @change="onSuitChange" />
</div>
<div ref="deck" class="deck flex flex-wrap justify-center gap-4" :class="{ 'pb-36': playerStore.currentTrack }">
<div ref="deck" class="deck flex flex-wrap justify-center gap-4" :class="{ 'pb-36': playerStore.currentTrack }"
@dragover.prevent @drop.prevent="handleGlobalDrop">
<card v-for="(track, i) in filteredTracks" :key="track.id" :track="track" :tabindex="i"
@card-click="playerStore.playPlaylistTrack(track)" :is-face-up="isCardRevealed(track.id)"
@click-card-symbol="openCardSharer()" />
@@ -18,7 +19,7 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { useDataStore } from '~/store/data'
import { useCardStore } from '~/store/card'
import { usePlayerStore } from '~/store/player'
@@ -48,7 +49,82 @@ const uiStore = useUiStore()
const deck = ref()
const tracks = computed(() => dataStore.getTracksByboxId(props.box.id))
const filteredTracks = ref(tracks.value)
// Suivre si un glisser est en cours depuis le bucket
const isDraggingFromBucket = ref(false)
// Gérer le dépôt d'une carte en dehors du bucket
const handleGlobalDrop = (e: DragEvent) => {
if (isDraggingFromBucket.value) {
e.preventDefault()
e.stopPropagation()
// Récupérer les données de la carte glissée
const cardData = e.dataTransfer?.getData('application/json')
if (cardData) {
try {
const track = JSON.parse(cardData)
// Retirer la carte du panier
cardStore.removeFromBucket(track.id)
// La carte réapparaîtra automatiquement dans la playlist
// grâce à la computed property filteredTracks
} catch (e) {
console.error('Erreur lors du traitement de la carte glissée', e)
}
}
}
isDraggingFromBucket.value = false
}
// Gérer le début du glisser depuis le bucket
const handleBucketDragStart = () => {
isDraggingFromBucket.value = true
}
// Configurer les écouteurs d'événements
onMounted(() => {
document.addEventListener('drop', handleGlobalDrop)
document.addEventListener('dragover', (e) => e.preventDefault()) // Nécessaire pour permettre le drop
document.addEventListener('bucket-drag-start', handleBucketDragStart)
})
// Nettoyer les écouteurs d'événements
onUnmounted(() => {
document.removeEventListener('drop', handleGlobalDrop)
document.removeEventListener('dragover', (e) => e.preventDefault())
document.removeEventListener('bucket-drag-start', handleBucketDragStart)
})
// Utiliser une computed property pour filteredTracks qui réagit aux changements
const filteredTracks = computed(() => {
let result = [...tracks.value]
// Exclure les pistes déjà dans le panier
result = result.filter(track => !cardStore.isInBucket(track.id))
// Appliquer les autres filtres
if (selectedSuit.value) {
result = result.filter(track => track.card?.suit === selectedSuit.value)
}
if (selectedRank.value) {
result = result.filter(track => track.card?.rank === selectedRank.value)
}
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
result = result.filter(track => {
const artistName = typeof track.artist === 'object' ? track.artist?.name : String(track.artist || '')
return (
track.title?.toLowerCase().includes(query) ||
artistName.toLowerCase().includes(query) ||
String(track.year || '').includes(query)
)
})
}
return result
})
// Variables réactives pour les filtres
const selectedSuit = ref('')
@@ -86,35 +162,11 @@ const onSearch = (query: string) => {
}
// Applique tous les filtres (couleur, rang et recherche)
// La computed property filteredTracks se mettra automatiquement à jour
// car elle dépend des mêmes réactifs que cette fonction
const applyFilters = () => {
let result = [...tracks.value]
// Filtre par couleur
if (selectedSuit.value) {
result = result.filter(track => track.card?.suit === selectedSuit.value)
}
// Filtre par rang
if (selectedRank.value) {
result = result.filter(track => track.card?.rank === selectedRank.value)
}
// Filtre par recherche textuelle
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
result = result.filter(track => {
// Gestion du nom d'artiste (peut être un objet ou une chaîne)
const artistName = typeof track.artist === 'object' ? track.artist?.name : String(track.artist || '')
// Recherche dans le titre, l'artiste et l'année
return (
track.title?.toLowerCase().includes(query) ||
artistName.toLowerCase().includes(query) ||
String(track.year || '').includes(query)
)
})
}
filteredTracks.value = result
// Cette fonction ne fait plus que déclencher la réévaluation des dépendances
// La computed property filteredTracks fera le reste
}
</script>