Compare commits
	
		
			2 Commits
		
	
	
		
			8ebda83a22
			...
			9771c799f2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9771c799f2 | ||
|  | 25d56ec4ef | 
| @@ -1,17 +1,13 @@ | |||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
|     <NuxtRouteAnnouncer /> |     <NuxtRouteAnnouncer /> | ||||||
|  |     <NuxtLayout> | ||||||
|       <NuxtPage /> |       <NuxtPage /> | ||||||
|     <SearchModal /> |     </NuxtLayout> | ||||||
|     <Loader /> |  | ||||||
|     <Player /> |  | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup> | <script setup> | ||||||
| import SearchModal from '~/components/SearchModal.vue' |  | ||||||
| import Player from '~/components/player.vue' |  | ||||||
| import Loader from '~/components/Loader.vue' |  | ||||||
| import { useUiStore } from '~/store/ui' | import { useUiStore } from '~/store/ui' | ||||||
| import { usePlayerStore } from '~/store/player' | import { usePlayerStore } from '~/store/player' | ||||||
| import { watch, computed } from 'vue' | import { watch, computed } from 'vue' | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="flex flex-col-reverse mt-16" :class="!!playerStore.currentTrack ? 'mb-36' : 'mb-16'"> |   <div class="boxes"> | ||||||
|     <box v-for="(box, i) in dataStore.boxes.slice()" :key="box.id" :tabindex="dataStore.boxes.length - i" :box="box" |     <box v-for="(box, i) in dataStore.boxes.slice()" :key="box.id" :tabindex="dataStore.boxes.length - i" :box="box" | ||||||
|       @click="onBoxClick(box)" class="text-center" :class="box.state" :id="box.id"> |       @click="onBoxClick(box)" class="text-center" :class="box.state" :id="box.id"> | ||||||
|       <button @click.stop="playSelectedBox(box)" v-if="box.state === 'box-selected'" |       <button @click.stop="playSelectedBox(box)" v-if="box.state === 'box-selected'" | ||||||
| @@ -12,7 +12,6 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref, onMounted } from 'vue' |  | ||||||
| import type { Box } from '~~/types/types' | import type { Box } from '~~/types/types' | ||||||
| import { useDataStore } from '~/store/data' | import { useDataStore } from '~/store/data' | ||||||
| import { usePlayerStore } from '~/store/player' | import { usePlayerStore } from '~/store/player' | ||||||
| @@ -40,32 +39,10 @@ function onBoxClick(b: Box) { | |||||||
| function playSelectedBox(b: Box) { | function playSelectedBox(b: Box) { | ||||||
|   playerStore.playBox(b) |   playerStore.playBox(b) | ||||||
| } | } | ||||||
|  |  | ||||||
| function KeyboardAction(e: KeyboardEvent) { |  | ||||||
|   switch (e.key) { |  | ||||||
|     case 'Escape': |  | ||||||
|       uiStore.closeBox() |  | ||||||
|       break; |  | ||||||
|     case 'ArrowUp': |  | ||||||
|       break; |  | ||||||
|  |  | ||||||
|     case 'Enter': |  | ||||||
|       if (document.activeElement?.id) { |  | ||||||
|         openBox(document.activeElement.id) |  | ||||||
|       } |  | ||||||
|       break; |  | ||||||
|     case 'ArrowDown': |  | ||||||
|       break; |  | ||||||
|     case 'ArrowLeft': |  | ||||||
|       break; |  | ||||||
|     case 'ArrowRight': |  | ||||||
|       break; |  | ||||||
|     default: |  | ||||||
|       break; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| onMounted(async () => { |  | ||||||
|   window.addEventListener('keydown', KeyboardAction) |  | ||||||
| }) |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss"> | ||||||
|  | .boxes { | ||||||
|  |   @apply flex flex-col-reverse; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|   | |||||||
| @@ -1,10 +1,9 @@ | |||||||
| <template> | <template> | ||||||
|   <article @click="() => playerStore.playTrack(props.track).catch(err => console.error(err))" |   <article class="card isplaying w-56 h-80" :class="isFaceUp ? 'face-up' : 'face-down'"> | ||||||
|     class="card flip-card isplaying w-56 h-80" :class="isFaceUp ? 'face-up' : 'face-down'"> |  | ||||||
|     <div class="flip-inner"> |     <div class="flip-inner"> | ||||||
|       <!-- Face-Up --> |       <!-- Face-Up --> | ||||||
|       <main |       <main | ||||||
|         class="flip-front backdrop-blur-sm border-1 -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"> |         class="face-up backdrop-blur-sm border-1 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"> | ||||||
|         <div class="flex items-center justify-center size-7 absolute top-7 right-7" v-if="isPlaylistTrack"> |         <div class="flex items-center justify-center size-7 absolute top-7 right-7" v-if="isPlaylistTrack"> | ||||||
|           <div class="suit text-7xl absolute" |           <div class="suit text-7xl absolute" | ||||||
|             :class="[isRedCard ? 'text-red-600' : 'text-slate-800', props.track.card?.suit]"> |             :class="[isRedCard ? 'text-red-600' : 'text-slate-800', props.track.card?.suit]"> | ||||||
| @@ -37,7 +36,7 @@ | |||||||
|  |  | ||||||
|       <!-- Face-Down --> |       <!-- Face-Down --> | ||||||
|       <footer |       <footer | ||||||
|         class="flip-back 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="face-down backdrop-blur-sm z-10 card w-56 h-80 p-3 bg-opacity-10 bg-white rounded-2xl shadow-lg flex flex-col overflow-hidden"> | ||||||
|         <div class="h-full flex p-16 text-center bg-slate-800 rounded-xl"> |         <div class="h-full flex p-16 text-center bg-slate-800 rounded-xl"> | ||||||
|           <img src="/favicon.svg" /> |           <img src="/favicon.svg" /> | ||||||
|           <div class="label label--id" v-if="isOrder"> |           <div class="label label--id" v-if="isOrder"> | ||||||
| @@ -52,12 +51,10 @@ | |||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import type { Track } from '~~/types/types' | import type { Track } from '~~/types/types' | ||||||
| import { usePlayerStore } from '~/store/player' |  | ||||||
|  |  | ||||||
| const props = withDefaults(defineProps<{ track: Track; isFaceUp?: boolean }>(), { | const props = withDefaults(defineProps<{ track: Track; isFaceUp?: boolean }>(), { | ||||||
|   isFaceUp: false |   isFaceUp: false | ||||||
| }) | }) | ||||||
| const playerStore = usePlayerStore() |  | ||||||
| const isManifesto = computed(() => props.track.boxId.startsWith('ES00')) | const isManifesto = computed(() => props.track.boxId.startsWith('ES00')) | ||||||
| const isOrder = computed(() => props.track.order && !isManifesto) | const isOrder = computed(() => props.track.order && !isManifesto) | ||||||
| const isPlaylistTrack = computed(() => props.track.type === 'playlist') | const isPlaylistTrack = computed(() => props.track.type === 'playlist') | ||||||
| @@ -75,7 +72,7 @@ const coverUrl = props.track.coverId.startsWith('http') | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Flip effect */ | /* Flip effect */ | ||||||
| .flip-card { | .card { | ||||||
|   perspective: 1000px; |   perspective: 1000px; | ||||||
|  |  | ||||||
|   .flip-inner { |   .flip-inner { | ||||||
| @@ -94,8 +91,8 @@ const coverUrl = props.track.coverId.startsWith('http') | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .flip-front, |   .face-down, | ||||||
|   .flip-back { |   .face-up { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     height: 100%; |     height: 100%; | ||||||
| @@ -103,11 +100,11 @@ const coverUrl = props.track.coverId.startsWith('http') | |||||||
|     will-change: transform; |     will-change: transform; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .flip-front { |   .face-up { | ||||||
|     transform: rotateY(0deg); |     transform: rotateY(0deg); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .flip-back { |   .face-down { | ||||||
|     transform: rotateY(180deg); |     transform: rotateY(180deg); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,12 +1,13 @@ | |||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
|     <div class="z-50 tools fixed top-0 -left-0 hidden"> |     <div class="deck-order"> | ||||||
|       <button @click="setDisplay('pile')">pile</button> |       <button @click="orderDeck('pile')">pile</button> | ||||||
|       <button @click="setDisplay('plateau')">plateau</button> |       <button @click="orderDeck('plateau')">plateau</button> | ||||||
|       <button @click="setDisplay('holdem')">holdem</button> |       <button @click="orderDeck('holdem')">holdem</button> | ||||||
|     </div> |     </div> | ||||||
|     <div ref="deck" class="deck flex flex-wrap justify-center gap-4"> |     <div ref="deck" class="deck flex flex-wrap justify-center gap-4"> | ||||||
|       <card v-for="(track, i) in tracks" :key="track.id" :track="track" tabindex="i" /> |       <card v-for="(track, i) in tracks" :key="track.id" :track="track" tabindex="i" | ||||||
|  |         @click="() => playerStore.playTrack(track).catch(err => console.error(err))" /> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| @@ -15,6 +16,7 @@ | |||||||
| import { computed, ref } from 'vue' | import { computed, ref } from 'vue' | ||||||
| import { useDataStore } from '~/store/data' | import { useDataStore } from '~/store/data' | ||||||
| import type { Box } from '~~/types/types' | import type { Box } from '~~/types/types' | ||||||
|  | import { usePlayerStore } from '~/store/player' | ||||||
|  |  | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
|   box: Box |   box: Box | ||||||
| @@ -22,10 +24,11 @@ const props = defineProps<{ | |||||||
| const dataStore = useDataStore() | const dataStore = useDataStore() | ||||||
| const deck = ref() | const deck = ref() | ||||||
| const tracks = computed(() => dataStore.getTracksByboxId(props.box.id)) | const tracks = computed(() => dataStore.getTracksByboxId(props.box.id)) | ||||||
|  | const playerStore = usePlayerStore() | ||||||
|  |  | ||||||
| function setDisplay(displayMode) { | function orderDeck(order: string) { | ||||||
|   deck.value.classList.remove('pile', 'plateau', 'holdem') |   deck.value.classList.remove('pile', 'plateau', 'holdem') | ||||||
|   deck.value.classList.add(displayMode) |   deck.value.classList.add(order) | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								app/layouts/default.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/layouts/default.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="w-full min-h-screen flex flex-col items-center bg-gray-50"> | ||||||
|  |     <!-- Header avec logo --> | ||||||
|  |     <header class="w-full py-4 px-6 bg-white shadow-sm"> | ||||||
|  |       <div class="max-w-7xl mx-auto w-full flex justify-center"> | ||||||
|  |         <div @click="navigateToHome" class="cursor-pointer inline-block"> | ||||||
|  |           <logo /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </header> | ||||||
|  |  | ||||||
|  |     <!-- Contenu principal --> | ||||||
|  |     <main class="w-full max-w-7xl flex-1 p-6"> | ||||||
|  |       <slot /> | ||||||
|  |     </main> | ||||||
|  |  | ||||||
|  |     <!-- Player de musique fixe en bas --> | ||||||
|  |     <SearchModal /> | ||||||
|  |     <Loader /> | ||||||
|  |     <Player class="w-full border-t border-gray-200" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { useRouter } from 'vue-router' | ||||||
|  |  | ||||||
|  | const router = useRouter() | ||||||
|  |  | ||||||
|  | const navigateToHome = () => { | ||||||
|  |   router.push('/') | ||||||
|  | } | ||||||
|  | </script> | ||||||
| @@ -1,12 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="w-full flex flex-col items-center"> |  | ||||||
|     <div @click="uiStore.closeBox()" class="cursor-pointer"> |  | ||||||
|       <logo /> |  | ||||||
|     </div> |  | ||||||
|     <main> |  | ||||||
|   <boxes /> |   <boxes /> | ||||||
|     </main> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| @@ -14,6 +7,12 @@ import { onMounted } from 'vue' | |||||||
| import { useRoute } from 'vue-router' | import { useRoute } from 'vue-router' | ||||||
| import { useUiStore } from '~/store/ui' | import { useUiStore } from '~/store/ui' | ||||||
| import { useDataStore } from '~/store/data' | import { useDataStore } from '~/store/data' | ||||||
|  | import { usePlayerStore } from '~/store/player' | ||||||
|  |  | ||||||
|  | // Configuration du layout | ||||||
|  | definePageMeta({ | ||||||
|  |   layout: 'default' | ||||||
|  | }) | ||||||
|  |  | ||||||
| const uiStore = useUiStore() | const uiStore = useUiStore() | ||||||
| const dataStore = useDataStore() | const dataStore = useDataStore() | ||||||
| @@ -24,6 +23,13 @@ onMounted(async () => { | |||||||
|   const idParam = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id |   const idParam = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id | ||||||
|   if (typeof idParam === 'string' && idParam.length > 0) { |   if (typeof idParam === 'string' && idParam.length > 0) { | ||||||
|     uiStore.selectBox(idParam) |     uiStore.selectBox(idParam) | ||||||
|  |  | ||||||
|  |     // Lire automatiquement la box si on est sur la page d'une box | ||||||
|  |     const box = dataStore.boxes.find(b => b.id === idParam) | ||||||
|  |     if (box) { | ||||||
|  |       const player = usePlayerStore() | ||||||
|  |       player.playBox(box).catch(console.error) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -1,17 +1,16 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="w-full flex flex-col items-center"> |  | ||||||
|     <div @click="uiStore.closeBox()" class="cursor-pointer"> |  | ||||||
|       <logo /> |  | ||||||
|     </div> |  | ||||||
|     <main> |  | ||||||
|   <boxes /> |   <boxes /> | ||||||
|     </main> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup> | <script setup> | ||||||
| import { useUiStore } from '~/store/ui' | import { useUiStore } from '~/store/ui' | ||||||
| import { useDataStore } from '~/store/data' | import { useDataStore } from '~/store/data' | ||||||
|  |  | ||||||
|  | // Configuration du layout | ||||||
|  | definePageMeta({ | ||||||
|  |   layout: 'default' | ||||||
|  | }) | ||||||
|  |  | ||||||
| const uiStore = useUiStore() | const uiStore = useUiStore() | ||||||
|  |  | ||||||
| onMounted(async () => { | onMounted(async () => { | ||||||
|   | |||||||
| @@ -1,12 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="w-full flex flex-col items-center"> |  | ||||||
|     <div @click="uiStore.closeBox()" class="cursor-pointer"> |  | ||||||
|       <logo /> |  | ||||||
|     </div> |  | ||||||
|     <main> |  | ||||||
|   <boxes /> |   <boxes /> | ||||||
|     </main> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup> | <script setup> | ||||||
| @@ -14,6 +7,11 @@ import { useUiStore } from '~/store/ui' | |||||||
| import { useDataStore } from '~/store/data' | import { useDataStore } from '~/store/data' | ||||||
| import { usePlayerStore } from '~/store/player' | import { usePlayerStore } from '~/store/player' | ||||||
|  |  | ||||||
|  | // Configuration du layout | ||||||
|  | definePageMeta({ | ||||||
|  |   layout: 'default' | ||||||
|  | }) | ||||||
|  |  | ||||||
| const uiStore = useUiStore() | const uiStore = useUiStore() | ||||||
| const dataStore = useDataStore() | const dataStore = useDataStore() | ||||||
| const playerStore = usePlayerStore() | const playerStore = usePlayerStore() | ||||||
|   | |||||||
| @@ -1,17 +0,0 @@ | |||||||
| import { useUiStore } from '~/store/ui' |  | ||||||
| export default defineNuxtPlugin((nuxtApp) => { |  | ||||||
|   const ui = useUiStore() |  | ||||||
|   const isMobile = nuxtApp.$isMobile as boolean | undefined |  | ||||||
|  |  | ||||||
|   const onKeyDown = (e: KeyboardEvent) => { |  | ||||||
|     if ((e.metaKey || e.ctrlKey) && (e.key === 'f' || e.key === 'F')) { |  | ||||||
|       if (isMobile) return |  | ||||||
|       e.preventDefault() |  | ||||||
|       if (!ui.showSearch) ui.openSearch() |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (process.client) { |  | ||||||
|     window.addEventListener('keydown', onKeyDown) |  | ||||||
|   } |  | ||||||
| }) |  | ||||||
							
								
								
									
										106
									
								
								app/plugins/shortcut.client.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								app/plugins/shortcut.client.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | import { useUiStore } from '~/store/ui' | ||||||
|  | import { usePlayerStore } from '~/store/player' | ||||||
|  | import { onBeforeUnmount, onUnmounted, onMounted } from 'vue' | ||||||
|  | import { useRoute } from 'vue-router' | ||||||
|  | import { useDataStore } from '~/store/data' | ||||||
|  |  | ||||||
|  | export default defineNuxtPlugin((nuxtApp) => { | ||||||
|  |   // Ne s'exécuter que côté client | ||||||
|  |   if (process.server) return | ||||||
|  |  | ||||||
|  |   const ui = useUiStore() | ||||||
|  |   const player = usePlayerStore() | ||||||
|  |   const route = useRoute() | ||||||
|  |   const dataStore = useDataStore() | ||||||
|  |  | ||||||
|  |   function isInputElement(target: EventTarget | null): boolean { | ||||||
|  |     return ( | ||||||
|  |       target instanceof HTMLInputElement || | ||||||
|  |       target instanceof HTMLTextAreaElement || | ||||||
|  |       target instanceof HTMLSelectElement || | ||||||
|  |       (target instanceof HTMLElement && target.isContentEditable) | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function handleKeyDown(e: KeyboardEvent) { | ||||||
|  |     console.log('Key pressed:', e.code, 'Key:', e.key, 'Target:', e.target) | ||||||
|  |  | ||||||
|  |     // Ne pas interférer avec les champs de formulaire | ||||||
|  |     if (isInputElement(e.target as HTMLElement)) { | ||||||
|  |       console.log('Input element, ignoring key') | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Gestion du raccourci de recherche (Ctrl+F / Cmd+F) | ||||||
|  |     if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'f') { | ||||||
|  |       e.preventDefault() | ||||||
|  |       if (!ui.showSearch) { | ||||||
|  |         ui.openSearch() | ||||||
|  |       } | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Gestion des autres touches uniquement si pas de touche de contrôle enfoncée | ||||||
|  |     if (e.ctrlKey || e.altKey || e.metaKey) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     switch (e.code) { | ||||||
|  |       // Gestion de la barre d'espace pour play/pause | ||||||
|  |       case 'Space': | ||||||
|  |         console.log('Space pressed, toggling play/pause') | ||||||
|  |         e.preventDefault() | ||||||
|  |         e.stopPropagation() | ||||||
|  |         if (player.currentTrack) { | ||||||
|  |           console.log('Toggling play state') | ||||||
|  |           player.togglePlay() | ||||||
|  |         } else { | ||||||
|  |           console.log('No current track to play/pause') | ||||||
|  |         } | ||||||
|  |         return false | ||||||
|  |  | ||||||
|  |       // Gestion de la touche Échap pour fermer la boîte | ||||||
|  |       case 'Escape': | ||||||
|  |         e.preventDefault() | ||||||
|  |         ui.closeBox() | ||||||
|  |         break | ||||||
|  |  | ||||||
|  |       // Gestion de la touche Entrée pour ouvrir une boîte | ||||||
|  |       case 'Enter': | ||||||
|  |         if (document.activeElement?.id) { | ||||||
|  |           e.preventDefault() | ||||||
|  |           ui.selectBox(document.activeElement.id) | ||||||
|  |           window.scrollTo({ top: 0, behavior: 'smooth' }) | ||||||
|  |         } | ||||||
|  |         break | ||||||
|  |  | ||||||
|  |       // Gestion des touches fléchées (à implémenter si nécessaire) | ||||||
|  |       case 'ArrowUp': | ||||||
|  |       case 'ArrowDown': | ||||||
|  |       case 'ArrowLeft': | ||||||
|  |       case 'ArrowRight': | ||||||
|  |         // Implémentation future de la navigation au clavier | ||||||
|  |         break | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Ajout de l'écouteur d'événements avec capture pour intercepter l'événement plus tôt | ||||||
|  |   window.addEventListener('keydown', handleKeyDown, { capture: true, passive: false }) | ||||||
|  |   console.log('Keyboard event listener added') | ||||||
|  |  | ||||||
|  |   // Nettoyage lors de la destruction | ||||||
|  |   const stop = () => { | ||||||
|  |     window.removeEventListener('keydown', handleKeyDown) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Nettoyage quand le composant est démonté | ||||||
|  |   onUnmounted(stop) | ||||||
|  |  | ||||||
|  |   // Nettoyage quand la page est déchargée | ||||||
|  |   if (process.client) { | ||||||
|  |     window.addEventListener('unload', stop) | ||||||
|  |     onBeforeUnmount(() => { | ||||||
|  |       window.removeEventListener('unload', stop) | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | }) | ||||||
		Reference in New Issue
	
	Block a user