2024-01-13 23:09:15 +00:00
|
|
|
<template>
|
2025-10-18 02:08:22 +00:00
|
|
|
<div class="recipe-search-box" @focusout="recipes = []">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
v-model="searchTerm"
|
|
|
|
|
@keyup.enter="search"
|
|
|
|
|
@keyup.exit="clear"
|
|
|
|
|
@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>
|
|
|
|
|
|
|
|
|
|
<script>
|
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
|
|
|
|
2024-01-13 23:09:15 +00:00
|
|
|
export default {
|
2025-10-18 02:08:22 +00:00
|
|
|
name: 'RecipeSearchBox',
|
|
|
|
|
components: { RecipeCard },
|
|
|
|
|
props: {
|
|
|
|
|
placeholder: { type: String, default: 'Add a recipe...' },
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
searchTerm: '',
|
|
|
|
|
recipes: [],
|
|
|
|
|
timeouts: [],
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
searchTerm() {
|
|
|
|
|
const searchTerm = this.searchTerm
|
|
|
|
|
if (searchTerm) {
|
|
|
|
|
this.timeouts.push(
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (searchTerm === this.searchTerm) {
|
|
|
|
|
this.search()
|
|
|
|
|
}
|
|
|
|
|
}, 200)
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-05-04 02:39:30 +00:00
|
|
|
},
|
2025-10-18 02:08:22 +00:00
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
async search() {
|
|
|
|
|
this.recipes = (await searchRecipes(this.searchTerm)) ?? this.recipes
|
2024-01-13 23:09:15 +00:00
|
|
|
},
|
2025-10-18 02:08:22 +00:00
|
|
|
selectRecipe(recipe) {
|
|
|
|
|
this.$emit('select-recipe', recipe)
|
|
|
|
|
this.searchTerm = ''
|
|
|
|
|
this.recipes = []
|
2024-01-13 23:09:15 +00:00
|
|
|
},
|
2025-10-18 02:08:22 +00:00
|
|
|
},
|
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>
|