improved global search dialog

This commit is contained in:
vabene1111
2024-03-05 20:43:18 +01:00
committed by smilerz
parent 40d460b458
commit da60b4a097

View File

@@ -1,12 +1,17 @@
<template> <template>
<slot name="activator"> <slot name="activator">
<v-btn @click="dialog = true"><i class="fas fa-search mr-1"></i> Search</v-btn> <v-btn @click="dialog = true" variant="plain" density="default">
<i class="fas fa-search mr-1"></i>
<span class="d-none d-sm-block">Search</span>
<v-chip size="x-small" variant="tonal" class="d-none d-md-flex ml-1" label>Ctrl+K</v-chip>
</v-btn>
</slot> </slot>
<v-dialog width="90%" max-width="800px" v-model="dialog" location="id_dialog_anchor" <v-dialog width="90%" max-width="800px" v-model="dialog" location="id_dialog_anchor"
location-strategy="connected"> location-strategy="connected">
<v-card> <v-card>
<!-- search input -->
<v-card-text class="pb-0"> <v-card-text class="pb-0">
<v-text-field <v-text-field
id="id_global_search_input" id="id_global_search_input"
@@ -18,17 +23,33 @@
variant="solo" variant="solo"
></v-text-field> ></v-text-field>
</v-card-text> </v-card-text>
<v-divider></v-divider>
<v-card-text>
<v-card :variant="cardVariant(index)" v-for="(item, index) in search_results" hover class="mt-1" @click="selected_result = index"> <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-title @click="goToSelectedRecipe()"> <v-card-title @click="goToSelectedRecipe()">
<v-avatar v-if="item.image" :image="item.image"></v-avatar> <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>
<v-icon :icon="item.icon" v-if="item.icon"></v-icon> <v-icon :icon="item.icon" v-if="item.icon"></v-icon>
{{ item.name }} {{ item.name }}
</v-card-title> </v-card-title>
</v-card> </v-card>
</v-card-text> </v-card-text>
<v-divider class="d-none d-sm-block"></v-divider>
<!-- keybind info shown on screens at least sm -->
<v-card-text class="d-none d-sm-block pt-2">
<v-chip size="x-small" class="mr-1" label><i class="fas fa-arrow-up"></i></v-chip>
<v-chip size="x-small" class="mr-1" label><i class="fas fa-arrow-down"></i></v-chip>
<small class="mr-2">to navigate</small>
<v-chip size="x-small" class="mr-1" label><i class="fas fa-level-down-alt fa-rotate-90"></i></v-chip>
<small class="mr-2">to select</small>
<v-chip size="x-small" class="mr-1" label> esc</v-chip>
<small>to close</small>
</v-card-text>
</v-card> </v-card>
@@ -49,6 +70,7 @@ export default defineComponent({
/** /**
* since dialog has no opened event watch the variable and focus input after delay (nextTick/directly does not work) * since dialog has no opened event watch the variable and focus input after delay (nextTick/directly does not work)
*/ */
this.search_query = ""
setTimeout(() => { setTimeout(() => {
if (newValue) { if (newValue) {
let search = document.getElementById('id_global_search_input') let search = document.getElementById('id_global_search_input')
@@ -59,6 +81,9 @@ export default defineComponent({
}, 20) }, 20)
}, },
search_query: function (newValue) { 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) { if (this.selected_result >= this.search_results.length) {
this.selected_result = this.search_results.length - 1 this.selected_result = this.search_results.length - 1
} }
@@ -74,6 +99,10 @@ export default defineComponent({
} }
}, },
computed: { 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 () { search_results: function () {
let search_results = [] as Array<SearchResult> let search_results = [] as Array<SearchResult>
@@ -97,6 +126,7 @@ export default defineComponent({
} }
}, },
mounted() { mounted() {
// add keyhandlers
window.addEventListener('keydown', (e) => { window.addEventListener('keydown', (e) => {
if (e.key == 'ArrowUp') { if (e.key == 'ArrowUp') {
this.selected_result = Math.max(0, this.selected_result - 1) this.selected_result = Math.max(0, this.selected_result - 1)
@@ -107,6 +137,10 @@ export default defineComponent({
if (e.key == 'Enter') { if (e.key == 'Enter') {
this.goToSelectedRecipe() this.goToSelectedRecipe()
} }
if (e.key == 'k' && e.ctrlKey) {
this.dialog = true
e.preventDefault()
}
}) })
const api = new ApiApi() const api = new ApiApi()
@@ -115,6 +149,10 @@ export default defineComponent({
}) })
}, },
methods: { methods: {
/**
* determines the style for selected elements
* @param index index of card to determine style for
*/
cardVariant(index: number) { cardVariant(index: number) {
if (this.selected_result == index) { if (this.selected_result == index) {
return 'tonal' return 'tonal'
@@ -122,6 +160,9 @@ export default defineComponent({
return 'elevated' return 'elevated'
} }
}, },
/**
* open selected recipe
*/
goToSelectedRecipe() { goToSelectedRecipe() {
this.dialog = false this.dialog = false
let searchResult = this.search_results[this.selected_result] let searchResult = this.search_results[this.selected_result]