mirror of
https://github.com/rjNemo/meal_planner
synced 2026-06-12 13:26:45 +00:00
use debounce search on search page
This commit is contained in:
parent
72824daf63
commit
6f5f66d02e
4 changed files with 93 additions and 62 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const searchQuery = ref((route.query.q as string) || "");
|
||||||
|
|
||||||
const { execute } = useRecipeRandom();
|
const { execute } = useRecipeRandom();
|
||||||
|
|
||||||
const handleRandomClick = async () => {
|
const handleRandomClick = async () => {
|
||||||
|
|
@ -9,6 +11,31 @@ const handleRandomClick = async () => {
|
||||||
}
|
}
|
||||||
await execute();
|
await execute();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const debouncedSearch = useDebounceFn(async (query: string) => {
|
||||||
|
if (searchQuery.value.trim()) {
|
||||||
|
router.push({
|
||||||
|
path: "/search",
|
||||||
|
query: { q: query.trim() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (searchQuery.value.trim()) {
|
||||||
|
router.push({
|
||||||
|
path: "/search",
|
||||||
|
query: { q: searchQuery.value.trim() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (route.path === "/search") {
|
||||||
|
// Watch for changes in searchQuery
|
||||||
|
watch(searchQuery, (newQuery) => {
|
||||||
|
debouncedSearch(newQuery);
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -49,7 +76,7 @@ const handleRandomClick = async () => {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-end gap-2">
|
<div class="navbar-end gap-2">
|
||||||
<recipe-search />
|
<recipe-search @search="handleSubmit" v-model="searchQuery" />
|
||||||
<button class="btn btn-primary" @click="handleRandomClick">Random</button>
|
<button class="btn btn-primary" @click="handleRandomClick">Random</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="grow"
|
class="grow"
|
||||||
placeholder="Search"
|
placeholder="Search recipes..."
|
||||||
v-model="searchQuery"
|
v-model="model"
|
||||||
@focus="isFocused = true"
|
@focus="isFocused = true"
|
||||||
@blur="isFocused = false"
|
@blur="isFocused = false"
|
||||||
|
@keydown.enter="$emit('search')"
|
||||||
/>
|
/>
|
||||||
<kbd class="kbd kbd-sm" :class="{ 'opacity-50': !isFocused }">⌘</kbd>
|
<kbd class="kbd kbd-sm" :class="{ 'opacity-50': !isFocused }">⌘</kbd>
|
||||||
<kbd class="kbd kbd-sm" :class="{ 'opacity-50': !isFocused }">K</kbd>
|
<kbd class="kbd kbd-sm" :class="{ 'opacity-50': !isFocused }">K</kbd>
|
||||||
|
|
@ -16,21 +17,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const searchQuery = ref("");
|
defineEmits(["search"]);
|
||||||
|
const model = defineModel<string>();
|
||||||
|
|
||||||
const isFocused = ref(false);
|
const isFocused = ref(false);
|
||||||
|
|
||||||
// Debounced search function
|
|
||||||
const debouncedSearch = useDebounceFn(async (query: string) => {
|
|
||||||
const { data, status, error } = await useRecipeSearch(query);
|
|
||||||
console.log("result", data.value, status.value, error.value);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
// Watch for changes in searchQuery
|
|
||||||
watch(searchQuery, (newQuery) => {
|
|
||||||
debouncedSearch(newQuery);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Optional: Handle keyboard shortcut
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const handleKeydown = (e: KeyboardEvent) => {
|
const handleKeydown = (e: KeyboardEvent) => {
|
||||||
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,26 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { Recipe } from "~/types/recipe";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const searchQuery = computed(() => route.query.q as string);
|
const searchQuery = computed(() => route.query.q as string);
|
||||||
|
const searchResults = ref<Recipe[]>([]);
|
||||||
|
const { data, status, error, execute } = await useRecipeSearch(
|
||||||
|
searchQuery.value || "",
|
||||||
|
);
|
||||||
|
|
||||||
const { data, status, error } = await useRecipeSearch(searchQuery.value || "");
|
searchResults.value = data.value;
|
||||||
|
|
||||||
|
watch(searchQuery, async (newQuery) => {
|
||||||
|
const { data, status, error, execute } = await useRecipeSearch(
|
||||||
|
newQuery.trim(),
|
||||||
|
);
|
||||||
|
searchResults.value = data.value;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container mx-auto px-4">
|
<div class="container mx-auto px-4">
|
||||||
<div v-if="status" class="flex justify-center my-8">
|
<div v-if="false" class="flex justify-center my-8">
|
||||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -33,7 +46,7 @@ const { data, status, error } = await useRecipeSearch(searchQuery.value || "");
|
||||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 my-8"
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 my-8"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="recipe in data"
|
v-for="recipe in searchResults"
|
||||||
:key="recipe.id"
|
:key="recipe.id"
|
||||||
class="card bg-base-100 shadow-xl"
|
class="card bg-base-100 shadow-xl"
|
||||||
>
|
>
|
||||||
|
|
@ -42,8 +55,8 @@ const { data, status, error } = await useRecipeSearch(searchQuery.value || "");
|
||||||
<h2 class="card-title">{{ recipe.title }}</h2>
|
<h2 class="card-title">{{ recipe.title }}</h2>
|
||||||
<p>{{ recipe.category }} • {{ recipe.origin }}</p>
|
<p>{{ recipe.category }} • {{ recipe.origin }}</p>
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
<NuxtLink :to="`/recipe/${recipe.id}`" class="btn btn-primary"
|
<nuxt-link :to="`/${recipe.id}`" class="btn btn-primary"
|
||||||
>View Recipe</NuxtLink
|
>View Recipe</nuxt-link
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -21,47 +21,47 @@ const mealSchema = z.object({
|
||||||
strMealThumb: z.string().url(),
|
strMealThumb: z.string().url(),
|
||||||
strTags: z.string().nullable(),
|
strTags: z.string().nullable(),
|
||||||
strYoutube: z.string(),
|
strYoutube: z.string(),
|
||||||
strIngredient1: z.string().optional(),
|
strIngredient1: z.string().nullish(),
|
||||||
strIngredient2: z.string().optional(),
|
strIngredient2: z.string().nullish(),
|
||||||
strIngredient3: z.string().optional(),
|
strIngredient3: z.string().nullish(),
|
||||||
strIngredient4: z.string().optional(),
|
strIngredient4: z.string().nullish(),
|
||||||
strIngredient5: z.string().optional(),
|
strIngredient5: z.string().nullish(),
|
||||||
strIngredient6: z.string().optional(),
|
strIngredient6: z.string().nullish(),
|
||||||
strIngredient7: z.string().optional(),
|
strIngredient7: z.string().nullish(),
|
||||||
strIngredient8: z.string().optional(),
|
strIngredient8: z.string().nullish(),
|
||||||
strIngredient9: z.string().optional(),
|
strIngredient9: z.string().nullish(),
|
||||||
strIngredient10: z.string().optional(),
|
strIngredient10: z.string().nullish(),
|
||||||
strIngredient11: z.string().optional(),
|
strIngredient11: z.string().nullish(),
|
||||||
strIngredient12: z.string().optional(),
|
strIngredient12: z.string().nullish(),
|
||||||
strIngredient13: z.string().optional(),
|
strIngredient13: z.string().nullish(),
|
||||||
strIngredient14: z.string().optional(),
|
strIngredient14: z.string().nullish(),
|
||||||
strIngredient15: z.string().optional(),
|
strIngredient15: z.string().nullish(),
|
||||||
strIngredient16: z.string().optional(),
|
strIngredient16: z.string().nullish(),
|
||||||
strIngredient17: z.string().optional(),
|
strIngredient17: z.string().nullish(),
|
||||||
strIngredient18: z.string().optional(),
|
strIngredient18: z.string().nullish(),
|
||||||
strIngredient19: z.string().optional(),
|
strIngredient19: z.string().nullish(),
|
||||||
strIngredient20: z.string().optional(),
|
strIngredient20: z.string().nullish(),
|
||||||
strMeasure1: z.string().optional(),
|
strMeasure1: z.string().nullish(),
|
||||||
strMeasure2: z.string().optional(),
|
strMeasure2: z.string().nullish(),
|
||||||
strMeasure3: z.string().optional(),
|
strMeasure3: z.string().nullish(),
|
||||||
strMeasure4: z.string().optional(),
|
strMeasure4: z.string().nullish(),
|
||||||
strMeasure5: z.string().optional(),
|
strMeasure5: z.string().nullish(),
|
||||||
strMeasure6: z.string().optional(),
|
strMeasure6: z.string().nullish(),
|
||||||
strMeasure7: z.string().optional(),
|
strMeasure7: z.string().nullish(),
|
||||||
strMeasure8: z.string().optional(),
|
strMeasure8: z.string().nullish(),
|
||||||
strMeasure9: z.string().optional(),
|
strMeasure9: z.string().nullish(),
|
||||||
strMeasure10: z.string().optional(),
|
strMeasure10: z.string().nullish(),
|
||||||
strMeasure11: z.string().optional(),
|
strMeasure11: z.string().nullish(),
|
||||||
strMeasure12: z.string().optional(),
|
strMeasure12: z.string().nullish(),
|
||||||
strMeasure13: z.string().optional(),
|
strMeasure13: z.string().nullish(),
|
||||||
strMeasure14: z.string().optional(),
|
strMeasure14: z.string().nullish(),
|
||||||
strMeasure15: z.string().optional(),
|
strMeasure15: z.string().nullish(),
|
||||||
strMeasure16: z.string().optional(),
|
strMeasure16: z.string().nullish(),
|
||||||
strMeasure17: z.string().optional(),
|
strMeasure17: z.string().nullish(),
|
||||||
strMeasure18: z.string().optional(),
|
strMeasure18: z.string().nullish(),
|
||||||
strMeasure19: z.string().optional(),
|
strMeasure19: z.string().nullish(),
|
||||||
strMeasure20: z.string().optional(),
|
strMeasure20: z.string().nullish(),
|
||||||
strSource: z.string().optional(),
|
strSource: z.string().nullish(),
|
||||||
strImageSource: z.string().nullable(),
|
strImageSource: z.string().nullable(),
|
||||||
strCreativeCommonsConfirmed: z.string().nullable(),
|
strCreativeCommonsConfirmed: z.string().nullable(),
|
||||||
dateModified: z.string().optional().nullable(),
|
dateModified: z.string().optional().nullable(),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue