<template> <div class="w-64 ml-8 relative"> <ui-text-input v-model="search" placeholder="Search.." @input="inputUpdate" @focus="focussed" @blur="blurred" class="w-full h-8 text-sm" /> <div class="absolute top-0 right-0 bottom-0 h-full flex items-center px-2 text-gray-400 cursor-pointer" @click="clickClear"> <span v-if="!search" class="material-icons" style="font-size: 1.2rem">search</span> <span v-else class="material-icons" style="font-size: 1.2rem">close</span> </div> <div v-show="showMenu && (lastSearch || isTyping)" class="absolute z-10 -mt-px w-full bg-bg border border-black-200 shadow-lg max-h-80 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"> <ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label"> <li v-if="isTyping" class="py-2 px-2"> <p>Typing...</p> </li> <li v-else-if="isFetching" class="py-2 px-2"> <p>Fetching...</p> </li> <li v-else-if="!items.length" class="py-2 px-2"> <p>No Results</p> </li> <template v-else> <template v-for="item in items"> <li :key="item.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400" role="option" @click="clickedOption(item)"> <template v-if="item.type === 'audiobook'"> <cards-audiobook-search-card :audiobook="item.data" /> </template> </li> </template> </template> </ul> </div> </div> </template> <script> export default { data() { return { showMenu: false, isFocused: false, focusTimeout: null, isTyping: false, isFetching: false, search: null, items: [], searchTimeout: null, lastSearch: null } }, computed: { audiobooks() { return this.$store.state.audiobooks.audiobooks } }, methods: { focussed() { this.isFocused = true this.showMenu = true }, blurred() { this.isFocused = false clearTimeout(this.focusTimeout) this.focusTimeout = setTimeout(() => { this.showMenu = false }, 200) }, async runSearch(value) { this.lastSearch = value if (!this.lastSearch) { return } this.isFetching = true var results = await this.$axios.$get(`/api/audiobooks?q=${value}`).catch((error) => { console.error('Search error', error) return [] }) this.isFetching = false this.items = results.map((res) => { return { id: res.id, data: res, type: 'audiobook' } }) }, inputUpdate(val) { clearTimeout(this.searchTimeout) if (!val) { this.lastSearch = '' this.isTyping = false return } this.isTyping = true this.searchTimeout = setTimeout(() => { this.isTyping = false this.runSearch(val) }, 1000) }, clickedOption(option) { if (option.type === 'audiobook') { this.$router.push(`/audiobook/${option.data.id}`) } }, clickClear() { if (this.search) { this.search = null this.items = [] this.showMenu = false } } }, mounted() {} } </script>