Quick Actions & global search #528
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
|
||||
<transition name="fade">
|
||||
<div class="search-results" v-if="searchResultsVisible">
|
||||
<div class="search-results" :class="{'search-results-inline': inline}" v-if="searchResultsVisible">
|
||||
<button
|
||||
v-if="creatableAvailable"
|
||||
class="is-fullwidth"
|
||||
|
@ -166,6 +166,27 @@ export default {
|
|||
return false
|
||||
},
|
||||
},
|
||||
// If true, displays the search results inline instead of using a dropdown.
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false
|
||||
},
|
||||
},
|
||||
// If true, shows search results when no query is specified.
|
||||
showEmpty: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return true
|
||||
},
|
||||
},
|
||||
// The delay in ms after which the search event will be fired. Used to avoid hitting the network on every keystroke.
|
||||
searchDelay: {
|
||||
type: Number,
|
||||
default() {
|
||||
return 200
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.hideSearchResultsHandler)
|
||||
|
@ -181,10 +202,14 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
searchResultsVisible() {
|
||||
if(this.query === '' && !this.showEmpty) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.showSearchResults && (
|
||||
(this.filteredSearchResults.length > 0) ||
|
||||
(this.creatable && this.query !== '')
|
||||
)
|
||||
(this.filteredSearchResults.length > 0) ||
|
||||
(this.creatable && this.query !== '')
|
||||
)
|
||||
},
|
||||
creatableAvailable() {
|
||||
return this.creatable && this.query !== '' && !this.filteredSearchResults.some(elem => {
|
||||
|
@ -211,7 +236,7 @@ export default {
|
|||
// Updating the query with a binding does not work on mobile for some reason,
|
||||
// getting the value manual does.
|
||||
this.query = this.$refs.searchInput.value
|
||||
|
||||
|
||||
if (this.searchTimeout !== null) {
|
||||
clearTimeout(this.searchTimeout)
|
||||
this.searchTimeout = null
|
||||
|
@ -225,7 +250,7 @@ export default {
|
|||
this.localLoading = false
|
||||
}, 100) // The duration of the loading timeout of the services
|
||||
this.showSearchResults = true
|
||||
}, 200)
|
||||
}, this.searchDelay)
|
||||
},
|
||||
hideSearchResultsHandler(e) {
|
||||
closeWhenClickedOutside(e, this.$refs.multiselectRoot, this.closeSearchResults)
|
||||
|
|
|
@ -1,53 +1,51 @@
|
|||
<template>
|
||||
<modal v-if="active">
|
||||
<div class="card p-4">
|
||||
<input
|
||||
type="text"
|
||||
class="input"
|
||||
<multiselect
|
||||
placeholder="What do you want to do?"
|
||||
v-focus
|
||||
@keyup="run"
|
||||
v-model="search"/>
|
||||
|
||||
<div v-if="search !== ''">
|
||||
<ul>
|
||||
<li v-for="l in lists" :key="l.id">{{ l.title }}</li>
|
||||
</ul>
|
||||
<nothing v-if="nothing">
|
||||
No results found.
|
||||
</nothing>
|
||||
</div>
|
||||
:search-results="results"
|
||||
label="title"
|
||||
@search="search"
|
||||
:inline="true"
|
||||
:show-empty="false"
|
||||
@select="select"
|
||||
:search-delay="0"
|
||||
/>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Multiselect from '@/components/input/multiselect'
|
||||
|
||||
import Nothing from '@/components/misc/nothing'
|
||||
export default {
|
||||
name: 'quick-actions',
|
||||
components: {Nothing},
|
||||
components: {
|
||||
Multiselect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: '',
|
||||
results: [],
|
||||
query: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
active: () => true, // TODO: use state + keyboard shortcut
|
||||
lists() {
|
||||
return Object.fromEntries(Object.entries(this.$store.state.lists).filter(l => {
|
||||
return l[1].title.toLowerCase().includes(this.search.toLowerCase())
|
||||
}))
|
||||
results() {
|
||||
return Object.values(this.$store.state.lists).filter(l => {
|
||||
return l.title.toLowerCase().includes(this.query.toLowerCase())
|
||||
}) ?? []
|
||||
},
|
||||
nothing() {
|
||||
return this.search === '' || Object.keys(this.lists).length === 0
|
||||
return this.search === '' || Object.keys(this.results).length === 0
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
run() {
|
||||
console.log('run', this.search)
|
||||
}
|
||||
search(query) {
|
||||
this.query = query
|
||||
},
|
||||
select(e) {
|
||||
console.log('select', e)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -72,6 +72,10 @@
|
|||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
|
||||
&-inline {
|
||||
position: static;
|
||||
}
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
display: block;
|
||||
|
|
Reference in New Issue