mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 20:28:46 -05:00
moved many compoents to composition API
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
<v-card-text class="pb-0">
|
||||
<v-text-field
|
||||
id="id_global_search_input"
|
||||
v-model="search_query"
|
||||
v-model="searchQuery"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
placeholder="Search"
|
||||
@@ -27,7 +27,7 @@
|
||||
<v-divider></v-divider>
|
||||
<!-- search results -->
|
||||
<v-card-text>
|
||||
<v-card :variant="cardVariant(index)" v-for="(item, index) in search_results" hover class="mt-1" @click="selected_result = index" :key="index">
|
||||
<v-card :variant="cardVariant(index)" v-for="(item, index) in searchResults" hover class="mt-1" @click="selectedResult = index" :key="index">
|
||||
<v-card-title @click="goToSelectedRecipe()">
|
||||
<v-avatar v-if="item.image" :image="item.image"></v-avatar>
|
||||
<v-avatar v-else-if="item.recipe_id !== undefined" color="tandoor">{{ item.name.charAt(0) }}</v-avatar>
|
||||
@@ -56,124 +56,120 @@
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script setup lang="ts">
|
||||
|
||||
import {defineComponent} from 'vue'
|
||||
import {computed, defineComponent, onMounted, ref, watch} from 'vue'
|
||||
import {SearchResult} from "@/types/SearchTypes";
|
||||
import {ApiApi, Recipe, RecipeFlat} from "@/openapi";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
export default defineComponent({
|
||||
name: "GlobalSearchDialog",
|
||||
props: {},
|
||||
watch: {
|
||||
dialog: function (newValue) {
|
||||
/**
|
||||
* since dialog has no opened event watch the variable and focus input after delay (nextTick/directly does not work)
|
||||
*/
|
||||
this.search_query = ""
|
||||
setTimeout(() => {
|
||||
if (newValue) {
|
||||
let search = document.getElementById('id_global_search_input')
|
||||
if (search != null) {
|
||||
search.focus()
|
||||
}
|
||||
}
|
||||
}, 20)
|
||||
},
|
||||
search_query: function (newValue) {
|
||||
/**
|
||||
* update selected result if search result length changes due to search_query changes
|
||||
*/
|
||||
if (this.selected_result >= this.search_results.length) {
|
||||
this.selected_result = this.search_results.length - 1
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
recipes: [] as Recipe[],
|
||||
flat_recipes: [] as RecipeFlat[],
|
||||
search_query: null as string|null,
|
||||
selected_result: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* build array of search results
|
||||
* uses custom type to be able to incorporate recent items, plans, books, ... at a later stage
|
||||
*/
|
||||
search_results: function () {
|
||||
let search_results = [] as Array<SearchResult>
|
||||
const router = useRouter()
|
||||
|
||||
if (this.search_query != '' && this.search_query != null) {
|
||||
search_results.push({name: this.search_query, icon: 'fas fa-search', suffix: 'Advanced Search'} as SearchResult)
|
||||
const dialog = ref(false)
|
||||
const recipes = ref([] as Recipe[])
|
||||
const flatRecipes = ref([] as RecipeFlat[])
|
||||
const searchQuery = ref(null as string | null)
|
||||
const selectedResult = ref(0)
|
||||
|
||||
this.flat_recipes.filter(fr => fr.name.toLowerCase().includes(this.search_query.toLowerCase())).slice(0, 10).forEach(r => {
|
||||
search_results.push({name: r.name, image: r.image, recipe_id: r.id} as SearchResult)
|
||||
})
|
||||
} else {
|
||||
// search_results.push({name: 'Recent 1', icon: 'fas fa-history',} as SearchResult)
|
||||
// search_results.push({name: 'Recent 2', icon: 'fas fa-history',} as SearchResult)
|
||||
// search_results.push({name: 'Recent 3', icon: 'fas fa-history',} as SearchResult)
|
||||
/**
|
||||
* build array of search results
|
||||
* uses custom type to be able to incorporate recent items, plans, books, ... at a later stage
|
||||
*/
|
||||
const searchResults = computed(() => {
|
||||
let search_results = [] as Array<SearchResult>
|
||||
|
||||
this.flat_recipes.slice(0, 5).forEach(r => {
|
||||
search_results.push({name: r.name, image: r.image, recipe_id: r.id} as SearchResult)
|
||||
})
|
||||
}
|
||||
if (searchQuery.value != '' && searchQuery.value != null) {
|
||||
search_results.push({name: searchQuery.value, icon: 'fas fa-search', suffix: 'Advanced Search'} as SearchResult)
|
||||
|
||||
return search_results
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// add keyhandlers
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (this.dialog) {
|
||||
if (e.key == 'ArrowUp') {
|
||||
this.selected_result = Math.max(0, this.selected_result - 1)
|
||||
}
|
||||
if (e.key == 'ArrowDown') {
|
||||
this.selected_result = Math.min(this.search_results.length, this.selected_result + 1)
|
||||
}
|
||||
if (e.key == 'Enter') {
|
||||
this.goToSelectedRecipe()
|
||||
}
|
||||
if (e.key == 'k' && e.ctrlKey) {
|
||||
this.dialog = true
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
flatRecipes.value.filter(fr => fr.name.toLowerCase().includes(searchQuery.value.toLowerCase())).slice(0, 10).forEach(r => {
|
||||
search_results.push({name: r.name, image: r.image, recipe_id: r.id} as SearchResult)
|
||||
})
|
||||
} else {
|
||||
// search_results.push({name: 'Recent 1', icon: 'fas fa-history',} as SearchResult)
|
||||
// search_results.push({name: 'Recent 2', icon: 'fas fa-history',} as SearchResult)
|
||||
// search_results.push({name: 'Recent 3', icon: 'fas fa-history',} as SearchResult)
|
||||
|
||||
const api = new ApiApi()
|
||||
api.apiRecipeFlatList().then(r => {
|
||||
this.flat_recipes = r
|
||||
flatRecipes.value.slice(0, 5).forEach(r => {
|
||||
search_results.push({name: r.name, image: r.image, recipe_id: r.id} as SearchResult)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* determines the style for selected elements
|
||||
* @param index index of card to determine style for
|
||||
*/
|
||||
cardVariant(index: number) {
|
||||
if (this.selected_result == index) {
|
||||
return 'tonal'
|
||||
} else {
|
||||
return 'elevated'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* open selected recipe
|
||||
*/
|
||||
goToSelectedRecipe() {
|
||||
this.dialog = false
|
||||
let searchResult = this.search_results[this.selected_result]
|
||||
if (searchResult.recipe_id != null) {
|
||||
this.$router.push({name: 'view_recipe', params: {'id': searchResult.recipe_id}})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return search_results
|
||||
|
||||
})
|
||||
|
||||
watch(dialog, (newValue) => {
|
||||
/**
|
||||
* since dialog has no opened event watch the variable and focus input after delay (nextTick/directly does not work)
|
||||
*/
|
||||
searchQuery.value = ""
|
||||
setTimeout(() => {
|
||||
if (newValue) {
|
||||
let search = document.getElementById('id_global_search_input')
|
||||
if (search != null) {
|
||||
search.focus()
|
||||
}
|
||||
}
|
||||
}, 20)
|
||||
})
|
||||
|
||||
watch(searchQuery, () => {
|
||||
/**
|
||||
* update selected result if search result length changes due to search_query changes
|
||||
*/
|
||||
if (selectedResult.value >= searchResults.value.length) {
|
||||
selectedResult.value = searchResults.value.length - 1
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (dialog.value) {
|
||||
if (e.key == 'ArrowUp') {
|
||||
selectedResult.value = Math.max(0, selectedResult.value - 1)
|
||||
}
|
||||
if (e.key == 'ArrowDown') {
|
||||
selectedResult.value = Math.min(searchResults.value.length, selectedResult.value + 1)
|
||||
}
|
||||
if (e.key == 'Enter') {
|
||||
goToSelectedRecipe()
|
||||
}
|
||||
} else {
|
||||
if (e.key == 'k' && e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
dialog.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const api = new ApiApi()
|
||||
api.apiRecipeFlatList().then(r => {
|
||||
flatRecipes.value = r
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* determines the style for selected elements
|
||||
* @param index index of card to determine style for
|
||||
*/
|
||||
function cardVariant(index: number) {
|
||||
if (selectedResult.value == index) {
|
||||
return 'tonal'
|
||||
} else {
|
||||
return 'elevated'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* open selected recipe
|
||||
*/
|
||||
function goToSelectedRecipe() {
|
||||
dialog.value = false
|
||||
let searchResult = searchResults.value[selectedResult.value]
|
||||
if (searchResult.recipe_id != null) {
|
||||
router.push({name: 'view_recipe', params: {'id': searchResult.recipe_id}})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<v-card-text>
|
||||
|
||||
<v-number-input v-model="mutable_number" @update:modelValue="updateNumber('set')" control-variant="split" :min="0" >
|
||||
<v-number-input v-model="mutable_number" @update:modelValue="updateNumber('set')" control-variant="split" :min="0" >
|
||||
</v-number-input>
|
||||
|
||||
<v-btn-group divided class="d-flex">
|
||||
@@ -53,7 +53,8 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
mutable_number: 0
|
||||
mutable_number: 0,
|
||||
someNumber: 12
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -82,7 +83,7 @@ export default defineComponent({
|
||||
if (operation === 'sub') {
|
||||
this.mutable_number = this.number - 1
|
||||
}
|
||||
|
||||
console.log(operation, this.mutable_number)
|
||||
this.$emit('change', {number: this.mutable_number})
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user