munch-ease-frontend/src/components/recipes/RecipeSearchBox.vue

127 lines
2.3 KiB
Vue
Raw Normal View History

2024-01-13 23:09:15 +00:00
<template>
2025-10-18 02:53:24 +00:00
<div class="recipe-search-box" @focusout="onFocusOut">
2025-10-18 02:08:22 +00:00
<input
type="text"
v-model="searchTerm"
@keyup.enter="search"
2025-10-18 02:53:24 +00:00
@keyup.esc="clear"
2025-10-18 02:08:22 +00:00
@focusin="search"
:placeholder="placeholder"
/>
<ul v-if="recipes?.length" class="dropdown">
<li
class="recipe"
v-for="recipe in recipes"
:key="recipe.id"
@mousedown="selectRecipe(recipe)"
>
<recipe-card :recipe="recipe" />
</li>
</ul>
</div>
2024-01-13 23:09:15 +00:00
</template>
2025-10-18 02:53:24 +00:00
<script setup>
import { ref, watch, onBeforeUnmount } from 'vue'
2025-10-18 01:36:55 +00:00
import { searchRecipes } from '@/api/recipes'
2025-10-18 02:08:22 +00:00
import RecipeCard from './RecipeCard.vue'
2024-01-14 01:46:45 +00:00
2025-10-18 02:53:24 +00:00
defineProps({
placeholder: { type: String, default: 'Add a recipe...' },
})
const emit = defineEmits(['select-recipe'])
const searchTerm = ref('')
const recipes = ref([])
let debounceId = null
watch(
searchTerm,
(newVal) => {
if (!newVal) {
recipes.value = []
if (debounceId) clearTimeout(debounceId)
return
2025-10-18 02:08:22 +00:00
}
2025-10-18 02:53:24 +00:00
if (debounceId) clearTimeout(debounceId)
debounceId = setTimeout(() => {
if (newVal === searchTerm.value) {
search()
2025-10-18 02:08:22 +00:00
}
2025-10-18 02:53:24 +00:00
}, 200)
}
)
async function search() {
const result = await searchRecipes(searchTerm.value)
recipes.value = result ?? recipes.value
}
function selectRecipe(recipe) {
emit('select-recipe', recipe)
searchTerm.value = ''
recipes.value = []
}
function clear() {
searchTerm.value = ''
recipes.value = []
2024-01-13 23:09:15 +00:00
}
2025-10-18 02:53:24 +00:00
function onFocusOut() {
recipes.value = []
}
onBeforeUnmount(() => {
if (debounceId) clearTimeout(debounceId)
})
2024-01-13 23:09:15 +00:00
</script>
<style scoped>
2024-01-14 01:46:45 +00:00
.recipe-search-box {
2025-10-18 02:08:22 +00:00
position: relative;
2024-01-14 01:46:45 +00:00
}
2024-01-13 23:09:15 +00:00
2024-01-14 01:46:45 +00:00
.recipe-search-box input {
2025-10-18 02:08:22 +00:00
width: calc(100% - 2em);
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
outline: none;
2024-01-13 23:09:15 +00:00
}
2024-01-14 01:46:45 +00:00
.recipe-search-box .dropdown {
2025-10-18 02:08:22 +00:00
position: absolute;
top: 100%;
width: 100%;
margin: auto;
background-color: #fff;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
list-style-type: none;
margin: 0;
padding: 0;
max-height: 40vh;
overflow-y: scroll;
2024-01-13 23:09:15 +00:00
}
2024-01-14 01:46:45 +00:00
.recipe-search-box .dropdown li {
2025-10-18 02:08:22 +00:00
padding: 8px;
cursor: pointer;
border-bottom: 1px solid #ccc;
2024-01-13 23:09:15 +00:00
}
2024-01-14 01:46:45 +00:00
.recipe-search-box .dropdown li:last-child {
2025-10-18 02:08:22 +00:00
border-bottom: none;
2024-01-13 23:09:15 +00:00
}
2024-01-14 01:46:45 +00:00
.recipe-search-box .dropdown li:hover {
2025-10-18 02:08:22 +00:00
background-color: #eee;
2024-01-13 23:09:15 +00:00
}
2025-10-18 02:08:22 +00:00
</style>