<template> <div class="autocomplete-container" :class="$vuetify.breakpoint.name"> <v-combobox v-model="select" :loading="loading" :items="items" :search-input.sync="search" item-text="label" :menu-props="{maxHeight: $vuetify.breakpoint.name === 'xs' ? 190 : 500, transition: 'fade-transition'}" prepend-inner-icon="search" :append-icon="null" return-object rounded hide-no-data auto-select-first no-filter hide-details label="Søk..." solo full-width flat outlined placeholder="Søk her" ref="autocomplete" color="primary" :dense="$vuetify.breakpoint.smAndDown" > <template v-slot:item="data"> <span class="search-hit">{{data.item.label}} </span> ({{data.item.lang_set ? Array.from(data.item.lang_set).sort().join(', ') : 'fritekstsøk'}}) </template> </v-combobox> </div> </template> <script> import axios from "axios" import debounce from "debounce" export default { props: { endpoint: String, }, data: function() { return { loading: false, items: [], search: null, select: null, suggesting: null, debounced: debounce(function(q, self) { self.loading = true return axios.get(self.endpoint + 'suggest?q=' + encodeURIComponent(q)) .then( function(response) { if (self.suggesting) { let hits = [] if (q == self.$refs.autocomplete.searchInput) { response.data.forEach((item, i) => { let match = encodeURIComponent(item.match) if (! hits[0] || hits[0].word != match) { hits.splice(0, 0, {q: encodeURIComponent(q), lang_set: new Set(), word: match, articles: []}) } hits[0].lang_set.add(item.dictionary == 'bob' ? 'bm' : 'nn') hits[0].articles.push(item) }); hits.forEach(function (hit) { if (hit.lang_set) { hit.label = decodeURIComponent(hit.word) } }); hits.reverse() hits = hits.slice(0, 9) hits.sort( (h1, h2) => { let val1 = h1.label.length * 10 + (h1.label[0].toLowerCase() === h1.label[0] ? 0 : 1) let val2 = h2.label.length * 10 + (h2.label[0].toLowerCase() === h2.label[0] ? 0 : 1) return val1 - val2 }) let currentSearch = self.$refs.autocomplete.searchInput if (q == currentSearch) { self.items = hits } self.items.push({currentSearch: encodeURIComponent(currentSearch), label: currentSearch + ' '}) } } self.loading = false }) }, 100) } }, watch: { search (val) { if (! val) { this.items = [] } else { this.run_query(val) } }, select(item) { if (item) { if (typeof item === 'string') { item = {"q": encodeURIComponent(item)} } this.items = [] this.suggesting = false this.$emit('submit', item) let self = this setTimeout(() => self.$refs.autocomplete.$refs.input.select(), 1) } } }, methods: { run_query(q) { this.items = [] this.suggesting = true this.debounced(q, this) } }, } </script> <style scoped> .search-hit { font-weight: bold; margin-right: 5px; color: var(--v-primary-base); } .autocomplete-container { padding-left: 10px; padding-right: 10px; } </style>