Last active
March 1, 2021 07:38
-
-
Save acidjazz/9fa1439cc9b52d1ccbf35ce5150aac65 to your computer and use it in GitHub Desktop.
fume search functionality
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Returns a function, that, as long as it continues to be invoked, will not | |
// be triggered. The function will be called after it stops being called for | |
// N milliseconds. If `immediate` is passed, trigger the function on the | |
// leading edge, instead of the trailing. | |
export function debounce (func, wait, immediate) { | |
let timeout | |
return function () { | |
const context = this; const args = arguments | |
const later = function () { | |
timeout = null | |
if (!immediate) func.apply(context, args) | |
} | |
const callNow = immediate && !timeout | |
clearTimeout(timeout) | |
timeout = setTimeout(later, wait) | |
if (callNow) func.apply(context, args) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="flex-1 flex justify-center lg:justify-end lg:mx-12"> | |
<div class="w-full px-2 lg:px-6"> | |
<label for="search" class="sr-only">Search fume (Press / to focus)</label> | |
<div class="relative text-indigo-300 focus-within:text-gray-400"> | |
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> | |
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20"> | |
<path | |
fill-rule="evenodd" | |
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" | |
clip-rule="evenodd" | |
/> | |
</svg> | |
</div> | |
<input | |
id="search" | |
ref="search" | |
v-model="q" | |
class="block w-full pl-10 pr-3 py-2 border border-transparent rounded-md leading-5 bg-indigo-400 bg-opacity-25 text-indigo-300 placeholder-indigo-300 focus:outline-none focus:bg-white focus:placeholder-gray-400 focus:text-gray-900 sm:text-sm transition duration-150 ease-in-out" | |
:class="{'rounded-b-none border-b-0': searching}" | |
autocomplete="off" | |
type="search" | |
placeholder="Press / to focus" | |
@input="debounce" | |
> | |
<div v-if="searching" v-on-clickaway="clear" class="origin-top-right absolute left-0 w-full z-10 text-gray-500"> | |
<div class="py-1 bg-white shadow-xl rounded-md rounded-t-none"> | |
<div v-if="results"> | |
<h3 class="px-3 text-xs leading-4 font-semibold text-gray-500 bg-gray-100 uppercase tracking-wider py-2"> | |
projects | |
</h3> | |
<search-result-project :projects="results.projects" @click.native="clear" /> | |
<h3 class="px-3 text-xs leading-4 font-semibold text-gray-500 bg-gray-100 border-t border-gray-200 uppercase tracking-wider py-2"> | |
environments | |
</h3> | |
<search-result-env :envs="results.envs" @click.native="clear" /> | |
<h3 class="px-3 text-xs leading-4 font-semibold text-gray-500 bg-gray-100 border-t border-gray-200 uppercase tracking-wider py-2"> | |
deployments | |
</h3> | |
<search-result-dep :deps="results.deps" @click.native="clear" /> | |
</div> | |
<div v-else> | |
<h3 class="px-3 text-xs leading-4 font-semibold text-gray-500 bg-gray-100 uppercase tracking-wider py-2"> | |
projects | |
</h3> | |
<div class="skeleton rounded w-1/3 my-2 mx-3"> | |
| |
</div> | |
<h3 class="px-3 text-xs leading-4 font-semibold text-gray-500 bg-gray-100 border-t border-gray-200 uppercase tracking-wider py-2"> | |
environments | |
</h3> | |
<div class="skeleton rounded w-1/2 my-2 mx-3"> | |
| |
</div> | |
<h3 class="px-3 text-xs leading-4 font-semibold text-gray-500 bg-gray-100 border-t border-gray-200 uppercase tracking-wider py-2"> | |
deployments | |
</h3> | |
<div class="skeleton rounded w-3/4 my-2 mx-3"> | |
| |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
import { mixin as clickAway } from 'vue-clickaway' | |
export default { | |
mixins: [ clickAway ], | |
data () { | |
return { | |
searching: false, | |
results: false, | |
loading: true, | |
debounce: this.$debounce(this.search, 250), | |
q: '', | |
} | |
}, | |
watch: { | |
'q' (after, before) { | |
if (after === '' && after !== before) this.clear() | |
}, | |
}, | |
mounted () { | |
this.listen() | |
}, | |
destroyed () { | |
this.remove() | |
}, | |
methods: { | |
listen () { | |
if (process.client) | |
window.addEventListener('keyup', this.check) | |
}, | |
remove () { | |
if (process.client) | |
window.removeEventListener('keydown', this.check) | |
}, | |
check (event) { | |
if ([ 'INPUT', 'TEXTAREA' ].includes(event.target.tagName)) return true | |
if (event.keyCode === 191 && this.$refs.search) this.$refs.search.focus() | |
}, | |
async search () { | |
if (this.q.length === 0) return true | |
this.results = false | |
this.loading = true | |
this.searching = true | |
await this.$sleep(2000) | |
this.results = (await this.$axios.get('search', { params: { q: this.q } })).data.data | |
this.loading = false | |
}, | |
clear () { | |
this.q = '' | |
this.searching = false | |
this.loading = false | |
this.results = false | |
}, | |
}, | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment