Bruno Predot

Gestion des events search et clear de la searchBar

@@ -6,7 +6,7 @@ import { useTMDB } from "~/composables/tMDB"; @@ -6,7 +6,7 @@ import { useTMDB } from "~/composables/tMDB";
6 import { Movie } from "~/models/movie"; 6 import { Movie } from "~/models/movie";
7 import { FilmIcon } from "lucide-vue-next"; 7 import { FilmIcon } from "lucide-vue-next";
8 import type { MovieInterface } from "~/interfaces/movie"; 8 import type { MovieInterface } from "~/interfaces/movie";
9 -import { useDateFormat } from '@vueuse/core' 9 +import { useDateFormat } from "@vueuse/core";
10 //#endregion 10 //#endregion
11 11
12 //#region --Declaration--. 12 //#region --Declaration--.
@@ -26,7 +26,7 @@ const observer = ref<IntersectionObserver | null>(null); @@ -26,7 +26,7 @@ const observer = ref<IntersectionObserver | null>(null);
26 26
27 //#region --Computed--. 27 //#region --Computed--.
28 const movies = computed(() => { 28 const movies = computed(() => {
29 - return useRepo(Movie).query().orderBy("popularity", 'desc').get() as unknown as MovieInterface[]; 29 + return useRepo(Movie).query().orderBy("popularity", "desc").get() as unknown as MovieInterface[];
30 }); 30 });
31 //#endregion 31 //#endregion
32 32
@@ -40,7 +40,11 @@ const fetchMovies = async (page: number) => { @@ -40,7 +40,11 @@ const fetchMovies = async (page: number) => {
40 isLoadingMore.value = true; 40 isLoadingMore.value = true;
41 const data = await fetchPopularMovies(page); 41 const data = await fetchPopularMovies(page);
42 // Save in Movie model. 42 // Save in Movie model.
43 - isInitialLoading.value ? useRepo(Movie).fresh(data.results) : useRepo(Movie).save(data.results); 43 + if (isInitialLoading.value) {
  44 + useRepo(Movie).fresh(data.results);
  45 + } else {
  46 + useRepo(Movie).save(data.results);
  47 + }
44 48
45 totalPages.value = data.total_pages; 49 totalPages.value = data.total_pages;
46 currentPage.value = page; 50 currentPage.value = page;
@@ -63,6 +67,15 @@ function createIntersectionObserver() { @@ -63,6 +67,15 @@ function createIntersectionObserver() {
63 { threshold: 1.0 }, 67 { threshold: 1.0 },
64 ); 68 );
65 } 69 }
  70 +
  71 +function handleSearchEvent(event: string) {
  72 + console.log(event);
  73 +}
  74 +
  75 +function handleClearSearchEvent() {
  76 + console.log("clear event");
  77 +}
  78 +
66 //#endregion 79 //#endregion
67 80
68 //#region --Global event--. 81 //#region --Global event--.
@@ -94,7 +107,11 @@ onBeforeUnmount(() => { @@ -94,7 +107,11 @@ onBeforeUnmount(() => {
94 <section> 107 <section>
95 <h1 class="text-4xl font-bold mb-8 text-center">Découvrez les films populaires</h1> 108 <h1 class="text-4xl font-bold mb-8 text-center">Découvrez les films populaires</h1>
96 <!-- Barre de recherche --> 109 <!-- Barre de recherche -->
97 - <search-bar placeholder="Rechercher un film..." /> 110 + <search-bar
  111 + placeholder="Rechercher un film..."
  112 + @event:search="handleSearchEvent"
  113 + @event:clear_search="handleClearSearchEvent"
  114 + />
98 <!-- Loading Skeleton --> 115 <!-- Loading Skeleton -->
99 <skeleton-movies-loader v-if="isInitialLoading" :is-initial-loading="isInitialLoading" :skeleton-number="20" /> 116 <skeleton-movies-loader v-if="isInitialLoading" :is-initial-loading="isInitialLoading" :skeleton-number="20" />
100 <!-- Liste des films --> 117 <!-- Liste des films -->
@@ -108,10 +125,10 @@ onBeforeUnmount(() => { @@ -108,10 +125,10 @@ onBeforeUnmount(() => {
108 <div class="relative pb-[150%]"> 125 <div class="relative pb-[150%]">
109 <img 126 <img
110 v-if="movie.poster_path" 127 v-if="movie.poster_path"
111 - :src="`https://image.tmdb.org/t/p/w500${movie.poster_path}`"  
112 :alt="movie.title" 128 :alt="movie.title"
  129 + :src="`https://image.tmdb.org/t/p/w500${movie.poster_path}`"
113 class="absolute inset-0 w-full h-full object-cover" 130 class="absolute inset-0 w-full h-full object-cover"
114 - > 131 + />
115 <div v-else class="absolute inset-0 w-full h-full bg-gray-700 flex items-center justify-center"> 132 <div v-else class="absolute inset-0 w-full h-full bg-gray-700 flex items-center justify-center">
116 <FilmIcon :size="48" class="text-gray-500" /> 133 <FilmIcon :size="48" class="text-gray-500" />
117 </div> 134 </div>
@@ -123,13 +140,13 @@ onBeforeUnmount(() => { @@ -123,13 +140,13 @@ onBeforeUnmount(() => {
123 </div> 140 </div>
124 <div class="p-4"> 141 <div class="p-4">
125 <h2 class="text-lg font-bold mb-1 line-clamp-1">{{ movie.title }}</h2> 142 <h2 class="text-lg font-bold mb-1 line-clamp-1">{{ movie.title }}</h2>
126 - <p class="text-sm text-gray-400">{{ useDateFormat(movie.release_date, 'DD-MM-YYYY') }}</p> 143 + <p class="text-sm text-gray-400">{{ useDateFormat(movie.release_date, "DD-MM-YYYY") }}</p>
127 </div> 144 </div>
128 </div> 145 </div>
129 </div> 146 </div>
130 147
131 <!-- Élément observé pour le défilement infini --> 148 <!-- Élément observé pour le défilement infini -->
132 - <div ref="loadMoreTrigger" class="h-10 mt-4"/> 149 + <div ref="loadMoreTrigger" class="h-10 mt-4" />
133 </section> 150 </section>
134 </template> 151 </template>
135 152
@@ -2,10 +2,11 @@ @@ -2,10 +2,11 @@
2 //#region --import--. 2 //#region --import--.
3 import { SearchIcon, XIcon } from "lucide-vue-next"; 3 import { SearchIcon, XIcon } from "lucide-vue-next";
4 import { ref } from "vue"; 4 import { ref } from "vue";
  5 +import { useDebounceFn } from "@vueuse/core";
5 //#endregion 6 //#endregion
6 7
7 //#region --Emits--. 8 //#region --Emits--.
8 -// const emit = defineEmits([]); 9 +const emit = defineEmits(['event:search', 'event:clear_search']);
9 //#endregion 10 //#endregion
10 11
11 //#region --Props--. 12 //#region --Props--.
@@ -22,6 +23,20 @@ defineProps({ @@ -22,6 +23,20 @@ defineProps({
22 //#region --Data/refs--. 23 //#region --Data/refs--.
23 const searchQuery = ref(""); 24 const searchQuery = ref("");
24 //#endregion 25 //#endregion
  26 +
  27 +//#region --Function--.
  28 +/**
  29 + * Debounced function
  30 + */
  31 +const handleSearchEvent = useDebounceFn(() => {
  32 + emit('event:search', searchQuery.value);
  33 +}, 500);
  34 +
  35 +function handleClearSearchEvent() {
  36 + searchQuery.value = '';
  37 + emit('event:clear_search')
  38 +}
  39 +//#endregion
25 </script> 40 </script>
26 41
27 <template> 42 <template>
@@ -33,12 +48,12 @@ const searchQuery = ref(""); @@ -33,12 +48,12 @@ const searchQuery = ref("");
33 :placeholder="placeholder" 48 :placeholder="placeholder"
34 class="w-full px-4 py-3 bg-gray-800 rounded-full text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary" 49 class="w-full px-4 py-3 bg-gray-800 rounded-full text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary"
35 type="text" 50 type="text"
36 - @input="console.log('debouncedSearch à prévoir')" 51 + @input="handleSearchEvent"
37 /> 52 />
38 <button 53 <button
39 v-if="searchQuery" 54 v-if="searchQuery"
40 class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white" 55 class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white"
41 - @click="console.log('clearSearch à prévoir')" 56 + @click="handleClearSearchEvent"
42 > 57 >
43 <XIcon :size="20" /> 58 <XIcon :size="20" />
44 </button> 59 </button>