bucket cards management
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="face back flex flex-row flex-wrap items-start p-4 overflow-hidden"
|
<div class="face back flex flex-row flex-wrap items-start p-4 overflow-hidden"
|
||||||
:class="{ 'overflow-y-scroll': !isCompilation }" ref="backFace">
|
: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">
|
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>
|
<span class="text-slate-700" v-if="isNotManifesto"> {{ track.order }}. </span>
|
||||||
<p class="text-left text-slate-700">
|
<p class="text-left text-slate-700">
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<br />
|
<br />
|
||||||
{{ track.artist.name }}
|
{{ track.artist.name }}
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="face right" ref="rightFace" />
|
<div class="face right" ref="rightFace" />
|
||||||
<div class="face left" ref="leftFace" />
|
<div class="face left" ref="leftFace" />
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ onMounted(() => {
|
|||||||
// Gestion du drag and drop desktop
|
// Gestion du drag and drop desktop
|
||||||
const handleDragStart = (event: { item: HTMLElement }) => {
|
const handleDragStart = (event: { item: HTMLElement }) => {
|
||||||
drag.value = true
|
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 }) => {
|
const handleDragEnd = (event: { item: HTMLElement; newIndex: number; oldIndex: number }) => {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
<div
|
<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"
|
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>
|
: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">
|
<div class="disc-label rounded-full bg-cover bg-center">
|
||||||
<img src="/favicon.svg" class="size-1/3">
|
<img src="/favicon.svg" class="size-1/3">
|
||||||
@@ -13,14 +15,14 @@
|
|||||||
<div class="spinner"></div>
|
<div class="spinner"></div>
|
||||||
</div>
|
</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 v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full h-1/5 text-base">
|
||||||
|
{{ platineStore.currentTrack?.title }}
|
||||||
|
<br>
|
||||||
|
{{ platineStore.currentTrack?.artist?.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
<SelectCardRank @change="onRankChange" />
|
<SelectCardRank @change="onRankChange" />
|
||||||
<SelectCardSuit @change="onSuitChange" />
|
<SelectCardSuit @change="onSuitChange" />
|
||||||
</div>
|
</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 v-for="(track, i) in filteredTracks" :key="track.id" :track="track" :tabindex="i"
|
||||||
@card-click="playerStore.playPlaylistTrack(track)" :is-face-up="isCardRevealed(track.id)"
|
@card-click="playerStore.playPlaylistTrack(track)" :is-face-up="isCardRevealed(track.id)"
|
||||||
@click-card-symbol="openCardSharer()" />
|
@click-card-symbol="openCardSharer()" />
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { useDataStore } from '~/store/data'
|
import { useDataStore } from '~/store/data'
|
||||||
import { useCardStore } from '~/store/card'
|
import { useCardStore } from '~/store/card'
|
||||||
import { usePlayerStore } from '~/store/player'
|
import { usePlayerStore } from '~/store/player'
|
||||||
@@ -48,7 +49,82 @@ const uiStore = useUiStore()
|
|||||||
|
|
||||||
const deck = ref()
|
const deck = ref()
|
||||||
const tracks = computed(() => dataStore.getTracksByboxId(props.box.id))
|
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
|
// Variables réactives pour les filtres
|
||||||
const selectedSuit = ref('')
|
const selectedSuit = ref('')
|
||||||
@@ -86,35 +162,11 @@ const onSearch = (query: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Applique tous les filtres (couleur, rang et recherche)
|
// 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 = () => {
|
const applyFilters = () => {
|
||||||
let result = [...tracks.value]
|
// Cette fonction ne fait plus que déclencher la réévaluation des dépendances
|
||||||
|
// La computed property filteredTracks fera le reste
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user