Remove multiselect again and diy

This commit is contained in:
kolaente 2021-05-30 16:28:21 +02:00
parent 4692a1046d
commit acd1b9b83a
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
3 changed files with 152 additions and 33 deletions

View File

@ -1,34 +1,51 @@
<template> <template>
<modal v-if="active"> <modal v-if="active" class="quick-actions">
<div class="card p-4"> <div class="card">
<multiselect <input
placeholder="What do you want to do?" v-focus
:search-results="results" class="input"
label="title" :class="{'is-loading': loading}"
@search="search" v-model="query"
:inline="true" placeholder="Type a command or search..."
:show-empty="false" @keyup="search"
@select="select" ref="searchInput"
:search-delay="0" @keydown.down.prevent="() => select(0, 0)"
/> />
<div class="results">
<div v-for="(r, k) in results" :key="k" class="result">
<span class="result-title">
{{ r.title }}
</span>
<div class="result-items">
<button
v-for="(i, key) in r.items"
:key="key"
:ref="`result-${k}_${key}`"
@keydown.up.prevent="() => select(k, key - 1)"
@keydown.down.prevent="() => select(k, key + 1)"
@click.prevent.stop="() => doAction(r.type, i)"
@keyup.prevent.enter="() => doAction(r.type, i)"
>
{{ i.title }}
</button>
</div>
</div>
</div>
</div> </div>
</modal> </modal>
</template> </template>
<script> <script>
import Multiselect from '@/components/input/multiselect'
import TaskService from '@/services/task' import TaskService from '@/services/task'
const TYPE_LIST = 'list' const TYPE_LIST = 'list'
const TYPE_TASK = 'task' const TYPE_TASK = 'task'
const TYPE_ACTION = 'action' const TYPE_ACTION = 'action'
const TYPE_TEAM = 'team'
export default { export default {
name: 'quick-actions', name: 'quick-actions',
components: {
Multiselect,
},
data() { data() {
return { return {
query: '', query: '',
@ -59,40 +76,59 @@ export default {
const lists = (Object.values(this.$store.state.lists).filter(l => { const lists = (Object.values(this.$store.state.lists).filter(l => {
return l.title.toLowerCase().includes(this.query.toLowerCase()) return l.title.toLowerCase().includes(this.query.toLowerCase())
}) ?? []) }) ?? [])
.map(l => {
l.type = TYPE_LIST
return l
})
const actions = this.availableActions const actions = this.availableActions
.filter(a => a.title.includes(this.query.toLowerCase())) .filter(a => a.title.toLowerCase().includes(this.query.toLowerCase()))
.map(a => {
a.type = TYPE_ACTION
return a
})
return [...actions, ...lists, ...this.foundTasks, ...this.foundTeams] return [
{
type: TYPE_ACTION,
title: 'Actions',
items: actions,
},
{
type: TYPE_TASK,
title: 'Tasks',
items: this.foundTasks,
},
{
type: TYPE_LIST,
title: 'Lists',
items: lists,
},
{
type: TYPE_TEAM,
title: 'Teams',
items: this.foundTeams,
},
].filter(i => i.items.length > 0)
}, },
nothing() { nothing() {
return this.search === '' || Object.keys(this.results).length === 0 return this.search === '' || Object.keys(this.results).length === 0
}, },
loading() {
return this.taskService.loading
},
}, },
created() { created() {
this.taskService = new TaskService() this.taskService = new TaskService()
}, },
methods: { methods: {
search(query) { search() {
this.query = query this.searchTasks()
this.searchTasks(query)
}, },
searchTasks(query) { searchTasks() {
if (this.query === '') {
return
}
if (this.taskSearchTimeout !== null) { if (this.taskSearchTimeout !== null) {
clearTimeout(this.taskSearchTimeout) clearTimeout(this.taskSearchTimeout)
this.taskSearchTimeout = null this.taskSearchTimeout = null
} }
this.taskSearchTimeout = setTimeout(() => { this.taskSearchTimeout = setTimeout(() => {
this.taskService.getAll({}, {s: query}) this.taskService.getAll({}, {s: this.query})
.then(r => { .then(r => {
r = r.map(t => { r = r.map(t => {
t.type = TYPE_TASK t.type = TYPE_TASK
@ -102,8 +138,8 @@ export default {
}) })
}, 150) }, 150)
}, },
select(e) { doAction(type, e) {
switch (e.type) { switch (type) {
case TYPE_LIST: case TYPE_LIST:
this.$router.push({name: 'list.index', params: {listId: e.id}}) this.$router.push({name: 'list.index', params: {listId: e.id}})
break break
@ -114,6 +150,35 @@ export default {
break break
} }
}, },
select(parentIndex, index) {
if (index < 0 && parentIndex === 0) {
this.$refs.searchInput.focus()
return
}
if (index < 0) {
parentIndex--;
index = this.results[parentIndex].items.length - 1
}
let elems = this.$refs[`result-${parentIndex}_${index}`]
if (this.results[parentIndex].items.length === index) {
elems = this.$refs[`result-${parentIndex + 1}_0`]
}
if (typeof elems === 'undefined' || elems.length === 0) {
return
}
if (Array.isArray(elems)) {
elems[0].focus()
return
}
elems.focus()
},
}, },
} }
</script> </script>

View File

@ -23,3 +23,4 @@
@import 'api-config'; @import 'api-config';
@import 'datepicker'; @import 'datepicker';
@import 'notifications'; @import 'notifications';
@import 'quick-actions';

View File

@ -0,0 +1,53 @@
.quick-actions {
.modal-content {
top: 6rem !important;
transform: translate(-50%, -3rem) !important;
}
.input {
border: 0;
font-size: 1.5rem;
}
.results {
text-align: left;
width: 100%;
color: $grey-800;
.result {
&-title {
background: $grey-50;
padding: .5rem;
display: block;
font-size: .75rem;
}
&-items {
button {
font-size: .9rem;
width: 100%;
background: transparent;
text-align: left;
box-shadow: none;
border-radius: 0;
text-transform: none;
font-family: $family-sans-serif;
font-weight: normal;
padding: .5rem .75rem;
border: none;
cursor: pointer;
&:focus, &:hover {
background: $grey-50;
box-shadow: none !important;
}
&:active {
background: $grey-100;
}
}
}
}
}
}