+
+ @card-click="playerStore.playPlaylistTrack(track)" :is-face-up="isCardRevealed(track.id)"
+ @click-card-symbol="openCardSharer()" />
@@ -27,10 +28,19 @@ import SelectCardSuit from '~/components/ui/SelectCardSuit.vue'
import SelectCardRank from '~/components/ui/SelectCardRank.vue'
import SearchInput from '~/components/ui/SearchInput.vue'
-const props = defineProps<{
- box: Box
+// Define the events this component emits
+const emit = defineEmits<{
+ (e: 'click', event: MouseEvent): void;
}>()
+const props = defineProps<{
+ box: Box;
+ class?: string;
+}>()
+
+// Use useAttrs to get all other attributes
+const attrs = useAttrs()
+
const cardStore = useCardStore()
const dataStore = useDataStore()
const playerStore = usePlayerStore()
@@ -47,12 +57,17 @@ const searchQuery = ref('')
const isCardRevealed = (trackId: number) => {
// Si une recherche est en cours, révéler automatiquement les cartes correspondantes
- if (searchQuery.value) return true
+ if (searchQuery.value || (selectedRank.value && selectedSuit.value)) return true
return cardStore.isCardRevealed(trackId)
}
-const closeDatBox = () => {
+const closeDatBox = (event: MouseEvent) => {
uiStore.closeBox()
+ emit('click', event)
+}
+
+const openCardSharer = () => {
+ uiStore.openCardSharer()
}
const onSuitChange = (suit: string) => {
@@ -107,9 +122,4 @@ const applyFilters = () => {
.deck {
position: relative;
}
-
-.docked {
- position: fixed;
- bottom: 0;
-}
-
\ No newline at end of file
+
diff --git a/app/components/SearchModal.vue b/app/components/ui/ModalSearch.vue
similarity index 100%
rename from app/components/SearchModal.vue
rename to app/components/ui/ModalSearch.vue
diff --git a/app/components/ui/ModalSharer.vue b/app/components/ui/ModalSharer.vue
new file mode 100644
index 0000000..2b4418e
--- /dev/null
+++ b/app/components/ui/ModalSharer.vue
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
Partager cette carte
+
+
+
+
+
+
![]()
+
+
{{ currentTrack.title }}
+
{{ typeof currentTrack.artist === 'object' ?
+ currentTrack.artist?.name : currentTrack.artist || 'Artiste inconnu' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/layouts/default.vue b/app/layouts/default.vue
index 6c5738b..a86315d 100644
--- a/app/layouts/default.vue
+++ b/app/layouts/default.vue
@@ -1,6 +1,6 @@
-
+
@@ -22,11 +22,25 @@ const onCardDropped = (card: Track) => {
.bucket {
z-index: 70;
- bottom: 0;
+ bottom: -260px;
+ transition: bottom 0.3s ease;
+ width: 100%;
+ overflow-x: scroll;
+
+ &:hover,
+ .card-dragging & {
+ bottom: 0;
+ }
+
+ .bucket-card-wrapper {
+ width: 100%;
+ }
}
.platine {
z-index: 60;
bottom: -70%;
+ transition: bottom 0.3s ease;
+ /* width: 25%; */
}
diff --git a/app/pages/boxes.vue b/app/pages/boxes.vue
deleted file mode 100644
index a094760..0000000
--- a/app/pages/boxes.vue
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/pages/card/[id].vue b/app/pages/card/[id].vue
new file mode 100644
index 0000000..a6999da
--- /dev/null
+++ b/app/pages/card/[id].vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+
+
+ {{ error }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/pages/index.vue b/app/pages/index.vue
index a92d3f7..a094760 100644
--- a/app/pages/index.vue
+++ b/app/pages/index.vue
@@ -1,4 +1,27 @@
-
-
+
+
+
+
+
diff --git a/app/pages/newsletter.vue b/app/pages/newsletter.vue
deleted file mode 100644
index daf2201..0000000
--- a/app/pages/newsletter.vue
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/app/pages/playground.vue b/app/pages/playground.vue
deleted file mode 100644
index 8aad56d..0000000
--- a/app/pages/playground.vue
+++ /dev/null
@@ -1,221 +0,0 @@
-
-
-
Card 3D Playground
-
-
-
-
-
3D Controls
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/pages/story/holo.vue b/app/pages/story/holo.vue
new file mode 100644
index 0000000..35fbf35
--- /dev/null
+++ b/app/pages/story/holo.vue
@@ -0,0 +1,3 @@
+
+ holo
+
\ No newline at end of file
diff --git a/app/pages/story/index.vue b/app/pages/story/index.vue
new file mode 100644
index 0000000..6d8a136
--- /dev/null
+++ b/app/pages/story/index.vue
@@ -0,0 +1,46 @@
+
+
+
Liste des pages Story
+
+ -
+
+ {{ page.name }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/pages/mix.vue b/app/pages/story/mix.vue
similarity index 100%
rename from app/pages/mix.vue
rename to app/pages/story/mix.vue
diff --git a/app/pages/story/test.vue b/app/pages/story/test.vue
new file mode 100644
index 0000000..a92d3f7
--- /dev/null
+++ b/app/pages/story/test.vue
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/plugins/01.atropos.ts b/app/plugins/01.atropos.ts
deleted file mode 100644
index 5a224ec..0000000
--- a/app/plugins/01.atropos.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { defineNuxtPlugin } from '#app'
-
-export default defineNuxtPlugin(() => {
- if (process.client) {
- import('atropos/element').then(({ default: AtroposComponent }) => {
- customElements.define('atropos-component', AtroposComponent)
- })
- }
-})
diff --git a/app/plugins/body-class.ts b/app/plugins/body-class.ts
new file mode 100644
index 0000000..8a55392
--- /dev/null
+++ b/app/plugins/body-class.ts
@@ -0,0 +1,52 @@
+import { defineNuxtPlugin } from '#app'
+import { useHead } from '#imports'
+
+export default defineNuxtPlugin((nuxtApp) => {
+ // Fonction pour ajouter une classe au body
+ const addBodyClass = (className: string) => {
+ if (process.client) {
+ document.body.classList.add(className)
+ } else {
+ // Pour le SSR, on utilise useHead
+ useHead({
+ bodyAttrs: {
+ class: className
+ }
+ })
+ }
+ }
+
+ // Fonction pour supprimer une classe du body
+ const removeBodyClass = (className: string) => {
+ if (process.client) {
+ document.body.classList.remove(className)
+ }
+ // Pas besoin de gérer la suppression côté SSR
+ }
+
+ // Fonction pour vérifier si une classe est présente
+ const hasBodyClass = (className: string) => {
+ if (process.client) {
+ return document.body.classList.contains(className)
+ }
+ return false
+ }
+
+ // Exposition des méthodes via le plugin
+ return {
+ provide: {
+ bodyClass: {
+ add: addBodyClass,
+ remove: removeBodyClass,
+ has: hasBodyClass,
+ toggle: (className: string) => {
+ if (hasBodyClass(className)) {
+ removeBodyClass(className)
+ } else {
+ addBodyClass(className)
+ }
+ }
+ }
+ }
+ }
+})
diff --git a/app/plugins/shortcut.client.ts b/app/plugins/keyboard.shortcut.client.ts
similarity index 98%
rename from app/plugins/shortcut.client.ts
rename to app/plugins/keyboard.shortcut.client.ts
index 97e2f47..45d19bf 100644
--- a/app/plugins/shortcut.client.ts
+++ b/app/plugins/keyboard.shortcut.client.ts
@@ -63,7 +63,7 @@ export default defineNuxtPlugin((nuxtApp) => {
}
break
- case 'Space':
+ case 'Space': // Espace pour play/pause
e.preventDefault()
e.stopPropagation()
diff --git a/app/store/card.ts b/app/store/card.ts
index d63b7ef..4263883 100644
--- a/app/store/card.ts
+++ b/app/store/card.ts
@@ -1,12 +1,23 @@
import { defineStore } from 'pinia'
import type { Track } from '~~/types/types'
+
export const useCardStore = defineStore('card', {
state: () => ({
// Stocke les IDs des cartes déjà révélées
- revealedCards: new Set
()
+ revealedCards: new Set(),
+ // Stocke les pistes dans le panier
+ bucket: [] as Track[],
+ // Stocke l'état d'ouverture du panier
+ isBucketOpen: false
}),
actions: {
+ // Mettre à jour l'ordre des pistes dans le panier
+ updateBucketOrder(newOrder: Track[]) {
+ this.bucket = [...newOrder]
+ this.saveBucketToLocalStorage()
+ },
+
// Marquer une carte comme révélée
revealCard(trackId: number) {
this.revealedCards.add(trackId)
@@ -18,9 +29,12 @@ export const useCardStore = defineStore('card', {
this.saveToLocalStorage()
},
- // Vérifier si une carte est révélée
- isCardRevealed(trackId: number): boolean {
- return this.revealedCards.has(trackId)
+ flipCard(track: any) {
+ if (this.isRevealed(track.id)) {
+ this.hideCard(track.id)
+ } else {
+ this.revealCard(track.id)
+ }
},
// Basculer l'état de révélation de toutes les cartes
@@ -76,6 +90,64 @@ export const useCardStore = defineStore('card', {
// Initialiser le store
initialize() {
this.loadFromLocalStorage()
+ },
+
+ // Gestion du panier
+ addToBucket(track: Track) {
+ // Vérifie si la piste n'est pas déjà dans le panier
+ if (!this.bucket.some((item) => item.id === track.id)) {
+ this.bucket.push(track)
+ this.saveBucketToLocalStorage()
+ }
+ },
+
+ removeFromBucket(trackId: number) {
+ const index = this.bucket.findIndex((item) => item.id === trackId)
+ if (index !== -1) {
+ this.bucket.splice(index, 1)
+ this.saveBucketToLocalStorage()
+ }
+ },
+
+ clearBucket() {
+ this.bucket = []
+ this.saveBucketToLocalStorage()
+ },
+
+ toggleBucket() {
+ this.isBucketOpen = !this.isBucketOpen
+ },
+
+ // Sauvegarder le panier dans le localStorage
+ saveBucketToLocalStorage() {
+ if (typeof window !== 'undefined') {
+ try {
+ localStorage.setItem('cardStoreBucket', JSON.stringify(this.bucket))
+ } catch (e) {
+ console.error('Failed to save bucket to localStorage', e)
+ }
+ }
+ },
+
+ // Charger le panier depuis le localStorage
+ loadBucketFromLocalStorage() {
+ if (typeof window !== 'undefined') {
+ try {
+ const saved = localStorage.getItem('cardStoreBucket')
+ if (saved) {
+ const bucket = JSON.parse(saved)
+ if (Array.isArray(bucket)) {
+ this.bucket = bucket
+ }
+ }
+ } catch (e) {
+ console.error('Failed to load bucket from localStorage', e)
+ }
+ }
+ },
+ // Vérifier si une carte est révélée
+ isCardRevealed(trackId: number): boolean {
+ return this.revealedCards.has(trackId)
}
},
@@ -83,6 +155,17 @@ export const useCardStore = defineStore('card', {
// Getter pour la réactivité dans les templates
isRevealed: (state) => (trackId: number) => {
return state.revealedCards.has(trackId)
+ },
+
+ // Getters pour le panier
+ bucketCount: (state) => state.bucket.length,
+
+ isInBucket: (state) => (trackId: number) => {
+ return state.bucket.some((track) => track.id === trackId)
+ },
+
+ bucketTotalDuration: (state) => {
+ return state.bucket.reduce((total, track) => total + ((track as any).duration || 0), 0)
}
}
})
diff --git a/app/store/platine.ts b/app/store/platine.ts
new file mode 100644
index 0000000..f520dd5
--- /dev/null
+++ b/app/store/platine.ts
@@ -0,0 +1,169 @@
+import { defineStore } from 'pinia'
+import type { Track } from '~~/types/types'
+import Disc from '~/platine-tools/disc'
+import Sampler from '~/platine-tools/sampler'
+import { useCardStore } from '~/store/card'
+
+export const usePlatineStore = defineStore('platine', () => {
+ // State
+ const currentTrack = ref