Skip to content

Instantly share code, notes, and snippets.

@acidjazz
Last active March 1, 2021 07:38
Show Gist options
  • Save acidjazz/9fa1439cc9b52d1ccbf35ce5150aac65 to your computer and use it in GitHub Desktop.
Save acidjazz/9fa1439cc9b52d1ccbf35ce5150aac65 to your computer and use it in GitHub Desktop.
fume search functionality
// 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)
}
}
<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">
&nbsp;
</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">
&nbsp;
</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">
&nbsp;
</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