improved search bar

This commit is contained in:
hay-kot 2021-03-04 21:24:05 -09:00
commit ff6959dcb2
2 changed files with 71 additions and 41 deletions

View file

@ -1,31 +1,37 @@
<template> <template>
<div> <v-menu v-model="menuModel" offset-y readonly max-width="450">
<v-autocomplete <template #activator="{ attrs }">
:items="autoResults" <v-text-field
v-model="searchSlug" class="mt-6"
item-value="item.slug" v-model="search"
item-text="item.name" v-bind="attrs"
dense dense
light light
:label="$t('search.search-mealie')" :label="$t('search.search-mealie')"
:search-input.sync="search"
hide-no-data
cache-items
solo solo
autofocus autofocus
auto-select-first style="max-width: 450px;"
@focus="onFocus"
> >
<template </v-text-field>
v-if="showResults" </template>
v-slot:item="{ item }" <v-card v-if="showResults" max-height="500" min-width="98%" class="">
style="max-width: 750px" <v-card-text class="py-1">Results</v-card-text>
<v-divider></v-divider>
<v-list scrollable>
<v-list-item
v-for="(item, index) in autoResults"
:key="index"
:to="showResults ? `/recipe/${item.item.slug}` : null"
> >
<v-list-item-avatar> <v-list-item-avatar>
<v-img :src="getImage(item.item.image)"></v-img> <v-img :src="getImage(item.item.image)"></v-img>
</v-list-item-avatar> </v-list-item-avatar>
<v-list-item-content @click="selected(item.item.slug)"> <v-list-item-content
<v-list-item-title> @click="showResults ? null : selected(item.item.slug)"
{{ item.item.name }} >
<v-list-item-title v-html="highlight(item.item.name)">
</v-list-item-title>
<v-rating <v-rating
dense dense
v-if="item.item.rating" v-if="item.item.rating"
@ -33,14 +39,13 @@
size="12" size="12"
> >
</v-rating> </v-rating>
</v-list-item-title> <v-list-item-subtitle v-html="highlight(item.item.description)">
<v-list-item-subtitle>
{{ item.item.description }}
</v-list-item-subtitle> </v-list-item-subtitle>
</v-list-item-content> </v-list-item-content>
</template> </v-list-item>
</v-autocomplete> </v-list>
</div> </v-card>
</v-menu>
</template> </template>
<script> <script>
@ -57,6 +62,7 @@ export default {
return { return {
searchSlug: "", searchSlug: "",
search: "", search: "",
menuModel: false,
data: [], data: [],
result: [], result: [],
autoResults: [], autoResults: [],
@ -66,9 +72,10 @@ export default {
threshold: 0.6, threshold: 0.6,
location: 0, location: 0,
distance: 100, distance: 100,
findAllMatches: true,
maxPatternLength: 32, maxPatternLength: 32,
minMatchCharLength: 1, minMatchCharLength: 2,
keys: ["name", "slug", "description"], keys: ["name", "description"],
}, },
}; };
}, },
@ -80,8 +87,15 @@ export default {
fuse() { fuse() {
return new Fuse(this.data, this.options); return new Fuse(this.data, this.options);
}, },
isSearching() {
return this.search && this.search.length > 0;
},
}, },
watch: { watch: {
isSearching(val) {
val ? (this.menuModel = true) : null;
},
search() { search() {
try { try {
this.result = this.fuse.search(this.search.trim()); this.result = this.fuse.search(this.search.trim());
@ -101,18 +115,34 @@ export default {
}, },
}, },
methods: { methods: {
highlight(string) {
if (!this.search) {
return string;
}
return string.replace(
new RegExp(this.search, "gi"),
match => `<mark>${match}</mark>`
);
},
getImage(image) { getImage(image) {
return utils.getImageURL(image); return utils.getImageURL(image);
}, },
selected(slug) { selected(slug) {
this.$emit("selected", slug); this.$emit("selected", slug);
}, },
async onFocus() {
clearTimeout(this.timeout);
this.isFocused = true;
},
}, },
}; };
</script> </script>
<style> <style scoped>
.color-transition { .color-transition {
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
} }
</style> </style>
<style lang="sass" scoped>
</style>

View file

@ -87,9 +87,6 @@ export default {
}, },
mounted() {}, mounted() {},
computed: { computed: {
loggedIn() {
return this.$store.getters.getIsLoggedIn;
},
filteredItems() { filteredItems() {
if (this.loggedIn) { if (this.loggedIn) {
return this.items.filter(x => x.restricted == true); return this.items.filter(x => x.restricted == true);
@ -97,6 +94,9 @@ export default {
return this.items.filter(x => x.restricted == false); return this.items.filter(x => x.restricted == false);
} }
}, },
loggedIn() {
return this.$store.getters.getIsLoggedIn;
},
}, },
methods: { methods: {