feat: quick-actions script setup (#2478)
Some checks failed
continuous-integration/drone/push Build is failing

Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: #2478
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
This commit is contained in:
Dominik Pschenitschni 2022-10-27 21:10:27 +00:00 committed by konrad
parent 9807858436
commit 386fd79b49

View File

@ -13,7 +13,7 @@
:placeholder="placeholder" :placeholder="placeholder"
@keyup="search" @keyup="search"
ref="searchInput" ref="searchInput"
@keydown.down.prevent="() => select(0, 0)" @keydown.down.prevent="select(0, 0)"
@keyup.prevent.delete="unselectCmd" @keyup.prevent.delete="unselectCmd"
@keyup.prevent.enter="doCmd" @keyup.prevent.enter="doCmd"
@keyup.prevent.esc="closeQuickActions" @keyup.prevent.esc="closeQuickActions"
@ -35,13 +35,14 @@
<BaseButton <BaseButton
v-for="(i, key) in r.items" v-for="(i, key) in r.items"
:key="key" :key="key"
:ref="`result-${k}_${key}`" class="result-item-button"
@keydown.up.prevent="() => select(k, key - 1)" :class="{'is-strikethrough': (i as DoAction<ITask>)?.done}"
@keydown.down.prevent="() => select(k, key + 1)" :ref="(el: Element | ComponentPublicInstance | null) => setResultRefs(el, k, key)"
@click.prevent.stop="() => doAction(r.type, i)" @keydown.up.prevent="select(k, key - 1)"
@keyup.prevent.enter="() => doAction(r.type, i)" @keydown.down.prevent="select(k, key + 1)"
@keyup.prevent.esc="() => $refs.searchInput.focus()" @click.prevent.stop="doAction(r.type, i)"
:class="{'is-strikethrough': i.done}" @keyup.prevent.enter="doAction(r.type, i)"
@keyup.prevent.esc="searchInput?.focus()"
> >
{{ i.title }} {{ i.title }}
</BaseButton> </BaseButton>
@ -52,23 +53,20 @@
</modal> </modal>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {ref, computed, watchEffect, shallowReactive, type ComponentPublicInstance} from 'vue'
import {useI18n} from 'vue-i18n'
import {useRouter} from 'vue-router'
import TaskService from '@/services/task' import TaskService from '@/services/task'
import TeamService from '@/services/team' import TeamService from '@/services/team'
import NamespaceModel from '@/models/namespace' import NamespaceModel from '@/models/namespace'
import TeamModel from '@/models/team' import TeamModel from '@/models/team'
import ListModel from '@/models/list' import ListModel from '@/models/list'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue' import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
import {getHistory} from '@/modules/listHistory'
import {parseTaskText, PrefixMode} from '@/modules/parseTaskText'
import {getQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
import {PREFIXES} from '@/modules/parseTaskText'
import {useBaseStore} from '@/stores/base' import {useBaseStore} from '@/stores/base'
import {useListStore} from '@/stores/lists' import {useListStore} from '@/stores/lists'
@ -76,429 +74,504 @@ import {useNamespaceStore} from '@/stores/namespaces'
import {useLabelStore} from '@/stores/labels' import {useLabelStore} from '@/stores/labels'
import {useTaskStore} from '@/stores/tasks' import {useTaskStore} from '@/stores/tasks'
const TYPE_LIST = 'list' import {getHistory} from '@/modules/listHistory'
const TYPE_TASK = 'task' import {parseTaskText, PrefixMode, PREFIXES} from '@/modules/parseTaskText'
const TYPE_CMD = 'cmd' import {getQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
const TYPE_TEAM = 'team' import {success} from '@/message'
const CMD_NEW_TASK = 'newTask' import type {ITeam} from '@/modelTypes/ITeam'
const CMD_NEW_LIST = 'newList' import type {ITask} from '@/modelTypes/ITask'
const CMD_NEW_NAMESPACE = 'newNamespace' import type {INamespace} from '@/modelTypes/INamespace'
const CMD_NEW_TEAM = 'newTeam' import type {IList} from '@/modelTypes/IList'
const SEARCH_MODE_ALL = 'all' const {t} = useI18n({useScope: 'global'})
const SEARCH_MODE_TASKS = 'tasks' const router = useRouter()
const SEARCH_MODE_LISTS = 'lists'
const SEARCH_MODE_TEAMS = 'teams'
export default defineComponent({ const baseStore = useBaseStore()
name: 'quick-actions', const listStore = useListStore()
components: { const namespaceStore = useNamespaceStore()
BaseButton, const labelStore = useLabelStore()
QuickAddMagic, const taskStore = useTaskStore()
},
data() {
return {
query: '',
selectedCmd: null,
foundTasks: [], type DoAction<Type = {}> = { type: ACTION_TYPE } & Type
taskSearchTimeout: null,
taskService: new TaskService(),
foundTeams: [], enum ACTION_TYPE {
teamSearchTimeout: null, CMD = 'cmd',
teamService: new TeamService(), TASK = 'task',
LIST = 'list',
TEAM = 'team',
}
enum COMMAND_TYPE {
NEW_TASK = 'newTask',
NEW_LIST = 'newList',
NEW_NAMESPACE = 'newNamespace',
NEW_TEAM = 'newTeam',
}
enum SEARCH_MODE {
ALL = 'all',
TASKS = 'tasks',
LISTS = 'lists',
TEAMS = 'teams',
}
const query = ref('')
const selectedCmd = ref<Command | null>(null)
const foundTasks = ref<DoAction<ITask>[]>([])
const taskService = shallowReactive(new TaskService())
const foundTeams = ref<ITeam[]>([])
const teamService = shallowReactive(new TeamService())
const active = computed(() => baseStore.quickActionsActive)
watchEffect(() => {
if (!active.value) {
reset()
} }
}, })
computed: {
active() { function closeQuickActions() {
const active = useBaseStore().quickActionsActive baseStore.setQuickActionsActive(false)
if (!active) { }
// FIXME: computeds shouldn't have side effects.
// create a watcher instead const foundLists = computed(() => {
this.reset() const { list } = parsedQuery.value
if (
searchMode.value === SEARCH_MODE.ALL ||
searchMode.value === SEARCH_MODE.LISTS ||
list === null
) {
return []
} }
return active
},
results() {
let lists = []
const listStore = useListStore()
if (this.searchMode === SEARCH_MODE_ALL || this.searchMode === SEARCH_MODE_LISTS) { const ncache: { [id: ListModel['id']]: INamespace } = {}
const {list} = this.parsedQuery
if (list === null) {
lists = []
} else {
const ncache = {}
const history = getHistory() const history = getHistory()
// Puts recently visited lists at the top const allLists = [
const allLists = [...new Set([ ...new Set([
...history.map(l => listStore.getListById(l.id)), ...history.map((l) => listStore.getListById(l.id)),
...listStore.searchList(list), ...listStore.searchList(list),
])] ]),
]
lists = allLists.filter(l => { return allLists.filter((l) => {
if (typeof l === 'undefined' || l === null) { if (typeof l === 'undefined' || l === null) {
return false return false
} }
if (typeof ncache[l.namespaceId] === 'undefined') { if (typeof ncache[l.namespaceId] === 'undefined') {
ncache[l.namespaceId] = useNamespaceStore().getNamespaceById(l.namespaceId) ncache[l.namespaceId] = namespaceStore.getNamespaceById(l.namespaceId)
} }
return !ncache[l.namespaceId].isArchived return !ncache[l.namespaceId].isArchived
}) })
} })
}
const cmds = this.availableCmds // FIXME: use fuzzysearch
.filter(a => a.title.toLowerCase().includes(this.query.toLowerCase())) const foundCommands = computed(() => availableCmds.value.filter((a) =>
a.title.toLowerCase().includes(query.value.toLowerCase()),
))
interface Result {
type: ACTION_TYPE
title: string
items: DoAction<any>
}
const results = computed<Result[]>(() => {
return [ return [
{ {
type: TYPE_CMD, type: ACTION_TYPE.CMD,
title: this.$t('quickActions.commands'), title: t('quickActions.commands'),
items: cmds, items: foundCommands.value,
}, },
{ {
type: TYPE_TASK, type: ACTION_TYPE.TASK,
title: this.$t('quickActions.tasks'), title: t('quickActions.tasks'),
items: this.foundTasks, items: foundTasks.value,
}, },
{ {
type: TYPE_LIST, type: ACTION_TYPE.LIST,
title: this.$t('quickActions.lists'), title: t('quickActions.lists'),
items: lists, items: foundLists.value,
}, },
{ {
type: TYPE_TEAM, type: ACTION_TYPE.TEAM,
title: this.$t('quickActions.teams'), title: t('quickActions.teams'),
items: this.foundTeams, items: foundTeams.value,
}, },
].filter(i => i.items.length > 0) ].filter((i) => i.items.length > 0)
}, })
nothing() {
return this.search === '' || Object.keys(this.results).length === 0
},
loading() {
return this.taskService.loading ||
useNamespaceStore().isLoading || useListStore().isLoading ||
this.teamService.loading
},
placeholder() {
if (this.selectedCmd !== null) {
switch (this.selectedCmd.action) {
case CMD_NEW_TASK:
return this.$t('quickActions.newTask')
case CMD_NEW_LIST:
return this.$t('quickActions.newList')
case CMD_NEW_NAMESPACE:
return this.$t('quickActions.newNamespace')
case CMD_NEW_TEAM:
return this.$t('quickActions.newTeam')
}
}
return this.$t('quickActions.placeholder') const loading = computed(() =>
taskService.loading ||
namespaceStore.isLoading ||
listStore.isLoading ||
teamService.loading,
)
interface Command {
type: COMMAND_TYPE
title: string
placeholder: string
action: () => Promise<void>
}
const commands = computed<{ [key in COMMAND_TYPE]: Command }>(() => ({
newTask: {
type: COMMAND_TYPE.NEW_TASK,
title: t('quickActions.cmds.newTask'),
placeholder: t('quickActions.newTask'),
action: newTask,
}, },
hintText() { newList: {
type: COMMAND_TYPE.NEW_LIST,
title: t('quickActions.cmds.newList'),
placeholder: t('quickActions.newList'),
action: newList,
},
newNamespace: {
type: COMMAND_TYPE.NEW_NAMESPACE,
title: t('quickActions.cmds.newNamespace'),
placeholder: t('quickActions.newNamespace'),
action: newNamespace,
},
newTeam: {
type: COMMAND_TYPE.NEW_TEAM,
title: t('quickActions.cmds.newTeam'),
placeholder: t('quickActions.newTeam'),
action: newTeam,
},
}))
const placeholder = computed(() => selectedCmd.value?.placeholder || t('quickActions.placeholder'))
const currentList = computed(() => Object.keys(baseStore.currentList).length === 0
? null
: baseStore.currentList,
)
const hintText = computed(() => {
let namespace let namespace
if (selectedCmd.value !== null && currentList.value !== null) {
if (this.selectedCmd !== null && this.currentList !== null) { switch (selectedCmd.value.type) {
switch (this.selectedCmd.action) { case COMMAND_TYPE.NEW_TASK:
case CMD_NEW_TASK: return t('quickActions.createTask', {
return this.$t('quickActions.createTask', {title: this.currentList.title}) title: currentList.value.title,
case CMD_NEW_LIST: })
namespace = useNamespaceStore().getNamespaceById(this.currentList.namespaceId) case COMMAND_TYPE.NEW_LIST:
return this.$t('quickActions.createList', {title: namespace.title}) namespace = namespaceStore.getNamespaceById(
currentList.value.namespaceId,
)
return t('quickActions.createList', {
title: namespace?.title,
})
} }
} }
const prefixes =
PREFIXES[getQuickAddMagicMode()] ?? PREFIXES[PrefixMode.Default]
return t('quickActions.hint', prefixes)
})
const prefixes = PREFIXES[getQuickAddMagicMode()] ?? PREFIXES[PrefixMode.Default] const availableCmds = computed(() => {
return this.$t('quickActions.hint', prefixes)
},
currentList() {
const currentList = useBaseStore().currentList
return Object.keys(currentList).length === 0 ? null : currentList
},
availableCmds() {
const cmds = [] const cmds = []
if (currentList.value !== null) {
if (this.currentList !== null) { cmds.push(commands.value.newTask, commands.value.newList)
cmds.push({
title: this.$t('quickActions.cmds.newTask'),
action: CMD_NEW_TASK,
})
cmds.push({
title: this.$t('quickActions.cmds.newList'),
action: CMD_NEW_LIST,
})
} }
cmds.push({ cmds.push(commands.value.newNamespace, commands.value.newTeam)
title: this.$t('quickActions.cmds.newNamespace'),
action: CMD_NEW_NAMESPACE,
})
cmds.push({
title: this.$t('quickActions.cmds.newTeam'),
action: CMD_NEW_TEAM,
})
return cmds return cmds
}, })
parsedQuery() {
return parseTaskText(this.query, getQuickAddMagicMode()) const parsedQuery = computed(() => parseTaskText(query.value, getQuickAddMagicMode()))
},
searchMode() { const searchMode = computed(() => {
if (this.query === '') { if (query.value === '') {
return SEARCH_MODE_ALL return SEARCH_MODE.ALL
} }
const { text, list, labels, assignees } = parsedQuery.value
const {text, list, labels, assignees} = this.parsedQuery
if (assignees.length === 0 && text !== '') { if (assignees.length === 0 && text !== '') {
return SEARCH_MODE_TASKS return SEARCH_MODE.TASKS
} }
if (assignees.length === 0 && list !== null && text === '' && labels.length === 0) { if (
return SEARCH_MODE_LISTS assignees.length === 0 &&
list !== null &&
text === '' &&
labels.length === 0
) {
return SEARCH_MODE.LISTS
} }
if (assignees.length > 0 && list === null && text === '' && labels.length === 0) { if (
return SEARCH_MODE_TEAMS assignees.length > 0 &&
list === null &&
text === '' &&
labels.length === 0
) {
return SEARCH_MODE.TEAMS
} }
return SEARCH_MODE.ALL
})
return SEARCH_MODE_ALL const isNewTaskCommand = computed(() => (
}, selectedCmd.value !== null &&
isNewTaskCommand() { selectedCmd.value.type === COMMAND_TYPE.NEW_TASK
return this.selectedCmd !== null && this.selectedCmd.action === CMD_NEW_TASK ))
},
}, const taskSearchTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
methods: {
search() { type Filter = {by: string, value: string | number, comparator: string}
this.searchTasks()
this.searchTeams() function filtersToParams(filters: Filter[]) {
}, const filter_by : Filter['by'][] = []
searchTasks() { const filter_value : Filter['value'][] = []
if (this.searchMode !== SEARCH_MODE_ALL && this.searchMode !== SEARCH_MODE_TASKS) { const filter_comparator : Filter['comparator'][] = []
this.foundTasks = []
filters.forEach(({by, value, comparator}) => {
filter_by.push(by)
filter_value.push(value)
filter_comparator.push(comparator)
})
return {
filter_by,
filter_value,
filter_comparator,
}
}
function searchTasks() {
if (
searchMode.value !== SEARCH_MODE.ALL &&
searchMode.value !== SEARCH_MODE.TASKS
) {
foundTasks.value = []
return return
} }
if (this.selectedCmd !== null) { if (selectedCmd.value !== null) {
return return
} }
if (this.taskSearchTimeout !== null) { if (taskSearchTimeout.value !== null) {
clearTimeout(this.taskSearchTimeout) clearTimeout(taskSearchTimeout.value)
this.taskSearchTimeout = null taskSearchTimeout.value = null
} }
const {text, list, labels} = this.parsedQuery const { text, list: listName, labels } = parsedQuery.value
const params = { const filters: Filter[] = []
s: text,
filter_by: [], // FIXME: improve types
filter_value: [], function addFilter(
filter_comparator: [], by: Filter['by'],
value: Filter['value'],
comparator: Filter['comparator'],
) {
filters.push({
by,
value,
comparator,
})
} }
const listStore = useListStore() if (listName !== null) {
const list = listStore.findListByExactname(listName)
if (list !== null) { if (list !== null) {
const l = listStore.findListByExactname(list) addFilter('listId', list.id, 'equals')
if (l !== null) {
params.filter_by.push('list_id')
params.filter_value.push(l.id)
params.filter_comparator.push('equals')
} }
} }
if (labels.length > 0) { if (labels.length > 0) {
const labelIds = useLabelStore().getLabelsByExactTitles(labels).map(l => l.id) const labelIds = labelStore.getLabelsByExactTitles(labels).map((l) => l.id)
if (labelIds.length > 0) { if (labelIds.length > 0) {
params.filter_by.push('labels') addFilter('labels', labelIds.join(), 'in')
params.filter_value.push(labelIds.join())
params.filter_comparator.push('in')
} }
} }
this.taskSearchTimeout = setTimeout(async () => { const params = {
const r = await this.taskService.getAll({}, params) s: text,
this.foundTasks = r.map(t => { ...filtersToParams(filters),
t.type = TYPE_TASK }
taskSearchTimeout.value = setTimeout(async () => {
const r = await taskService.getAll({}, params) as DoAction<ITask>[]
foundTasks.value = r.map((t) => {
t.type = ACTION_TYPE.TASK
const list = listStore.getListById(t.listId) const list = listStore.getListById(t.listId)
if (list !== null) { if (list !== null) {
t.title = `${t.title} (${list.title})` t.title = `${t.title} (${list.title})`
} }
return t return t
}) })
}, 150) }, 150)
}, }
searchTeams() {
if (this.searchMode !== SEARCH_MODE_ALL && this.searchMode !== SEARCH_MODE_TEAMS) { const teamSearchTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
this.foundTeams = []
function searchTeams() {
if (
searchMode.value !== SEARCH_MODE.ALL &&
searchMode.value !== SEARCH_MODE.TEAMS
) {
foundTeams.value = []
return return
} }
if (query.value === '' || selectedCmd.value !== null) {
if (this.query === '' || this.selectedCmd !== null) {
return return
} }
if (teamSearchTimeout.value !== null) {
if (this.teamSearchTimeout !== null) { clearTimeout(teamSearchTimeout.value)
clearTimeout(this.teamSearchTimeout) teamSearchTimeout.value = null
this.teamSearchTimeout = null
} }
const { assignees } = parsedQuery.value
const {assignees} = this.parsedQuery teamSearchTimeout.value = setTimeout(async () => {
const teamSearchPromises = assignees.map((t) =>
this.teamSearchTimeout = setTimeout(async () => { teamService.getAll({}, { s: t }),
const teamSearchPromises = assignees.map((t) => this.teamService.getAll({}, {s: t})) )
const teamsResult = await Promise.all(teamSearchPromises) const teamsResult = await Promise.all(teamSearchPromises)
this.foundTeams = teamsResult.flatMap(team => { foundTeams.value = teamsResult.flatMap((team) => {
team.title = team.name team.title = team.name
return team return team
}) })
}, 150) }, 150)
}, }
closeQuickActions() {
useBaseStore().setQuickActionsActive(false) function search() {
}, searchTasks()
doAction(type, item) { searchTeams()
}
const searchInput = ref<HTMLElement | null>(null)
async function doAction(type: ACTION_TYPE, item: DoAction) {
switch (type) { switch (type) {
case TYPE_LIST: case ACTION_TYPE.LIST:
this.$router.push({name: 'list.index', params: {listId: item.id}}) closeQuickActions()
this.closeQuickActions() await router.push({
name: 'list.index',
params: { listId: (item as DoAction<IList>).id },
})
break break
case TYPE_TASK: case ACTION_TYPE.TASK:
this.$router.push({name: 'task.detail', params: {id: item.id}}) closeQuickActions()
this.closeQuickActions() await router.push({
name: 'task.detail',
params: { id: (item as DoAction<ITask>).id },
})
break break
case TYPE_CMD: case ACTION_TYPE.CMD:
this.query = '' query.value = ''
this.selectedCmd = item selectedCmd.value = item as DoAction<Command>
this.$refs.searchInput.focus() searchInput.value?.focus()
break break
} }
}, }
doCmd() {
if (this.results.length === 1 && this.results[0].items.length === 1) { async function doCmd() {
this.doAction(this.results[0].type, this.results[0].items[0]) if (results.value.length === 1 && results.value[0].items.length === 1) {
const result = results.value[0]
doAction(result.type, result.items[0])
return return
} }
if (this.selectedCmd === null) { if (selectedCmd.value === null || query.value === '') {
return return
} }
if (this.query === '') { closeQuickActions()
await selectedCmd.value.action()
}
async function newTask() {
if (currentList.value === null) {
return return
} }
switch (this.selectedCmd.action) {
case CMD_NEW_TASK:
this.newTask()
break
case CMD_NEW_LIST:
this.newList()
break
case CMD_NEW_NAMESPACE:
this.newNamespace()
break
case CMD_NEW_TEAM:
this.newTeam()
break
}
},
async newTask() {
if (this.currentList === null) {
return
}
const taskStore = useTaskStore()
const task = await taskStore.createNewTask({ const task = await taskStore.createNewTask({
title: this.query, title: query.value,
listId: this.currentList.id, listId: currentList.value.id,
}) })
this.$message.success({message: this.$t('task.createSuccess')}) success({ message: t('task.createSuccess') })
this.$router.push({name: 'task.detail', params: {id: task.id}}) await router.push({ name: 'task.detail', params: { id: task.id } })
this.closeQuickActions() }
},
async newList() { async function newList() {
if (this.currentList === null) { if (currentList.value === null) {
return return
} }
const newList = await listStore.createList(new ListModel({
const newList = new ListModel({ title: query.value,
title: this.query, namespaceId: currentList.value.namespaceId,
namespaceId: this.currentList.namespaceId, }))
success({ message: t('list.create.createdSuccess')})
await router.push({
name: 'list.index',
params: { listId: newList.id },
}) })
const listStore = useListStore() }
const list = await listStore.createList(newList)
this.$message.success({message: this.$t('list.create.createdSuccess')})
this.$router.push({name: 'list.index', params: {listId: list.id}})
this.closeQuickActions()
},
async newNamespace() {
const newNamespace = new NamespaceModel({title: this.query})
await useNamespaceStore().createNamespace(newNamespace) async function newNamespace() {
this.$message.success({message: this.$t('namespace.create.success')}) const newNamespace = new NamespaceModel({ title: query.value })
this.closeQuickActions() await namespaceStore.createNamespace(newNamespace)
}, success({ message: t('namespace.create.success') })
async newTeam() { }
const newTeam = new TeamModel({name: this.query})
const team = await this.teamService.create(newTeam) async function newTeam() {
this.$router.push({ const newTeam = new TeamModel({ name: query.value })
const team = await teamService.create(newTeam)
await router.push({
name: 'teams.edit', name: 'teams.edit',
params: {id: team.id}, params: { id: team.id },
}) })
this.$message.success({message: this.$t('team.create.success')}) success({ message: t('team.create.success') })
this.closeQuickActions() }
},
select(parentIndex, index) {
if (index < 0 && parentIndex === 0) { type BaseButtonInstance = InstanceType<typeof BaseButton>
this.$refs.searchInput.focus() const resultRefs = ref<(BaseButtonInstance | null)[][]>([])
return
function setResultRefs(el: Element | ComponentPublicInstance | null, index: number, key: number) {
if (resultRefs.value[index] === undefined) {
resultRefs.value[index] = []
} }
resultRefs.value[index][key] = el as (BaseButtonInstance | null)
}
function select(parentIndex: number, index: number) {
if (index < 0 && parentIndex === 0) {
searchInput.value?.focus()
return
}
if (index < 0) { if (index < 0) {
parentIndex-- parentIndex--
index = this.results[parentIndex].items.length - 1 index = results.value[parentIndex].items.length - 1
} }
let elems = resultRefs.value[parentIndex][index]
let elems = this.$refs[`result-${parentIndex}_${index}`] if (results.value[parentIndex].items.length === index) {
elems = resultRefs.value[parentIndex + 1][0]
if (this.results[parentIndex].items.length === index) {
elems = this.$refs[`result-${parentIndex + 1}_0`]
} }
if (
if (typeof elems === 'undefined' || elems.length === 0) { typeof elems === 'undefined'
/* || elems.length === 0 */
) {
return return
} }
if (Array.isArray(elems)) { if (Array.isArray(elems)) {
elems[0].focus() elems[0].focus()
return return
} }
elems?.focus()
}
elems.focus() function unselectCmd() {
}, if (query.value !== '') {
unselectCmd() {
if (this.query !== '') {
return return
} }
selectedCmd.value = null
}
this.selectedCmd = null function reset() {
}, query.value = ''
reset() { selectedCmd.value = null
this.query = '' }
this.selectedCmd = null
},
},
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -508,8 +581,9 @@ export default defineComponent({
top: 3rem; top: 3rem;
transform: translate(-50%, 0); transform: translate(-50%, 0);
} }
}
.action-input { .action-input {
display: flex; display: flex;
align-items: center; align-items: center;
@ -522,29 +596,28 @@ export default defineComponent({
padding-left: .5rem; padding-left: .5rem;
} }
.active-cmd { }
.active-cmd {
font-size: 1.25rem; font-size: 1.25rem;
margin-left: .5rem; margin-left: .5rem;
background-color: var(--grey-100); background-color: var(--grey-100);
color: var(--grey-800); color: var(--grey-800);
} }
}
.results { .results {
text-align: left; text-align: left;
width: 100%; width: 100%;
color: var(--grey-800); color: var(--grey-800);
}
.result { .result-title {
&-title {
background: var(--grey-100); background: var(--grey-100);
padding: .5rem; padding: .5rem;
display: block; display: block;
font-size: .75rem; font-size: .75rem;
} }
&-items { .result-item-button {
button {
font-size: .9rem; font-size: .9rem;
width: 100%; width: 100%;
background: transparent; background: transparent;
@ -559,7 +632,8 @@ export default defineComponent({
border: none; border: none;
cursor: pointer; cursor: pointer;
&:focus, &:hover { &:focus,
&:hover {
background: var(--grey-100); background: var(--grey-100);
box-shadow: none !important; box-shadow: none !important;
} }
@ -567,10 +641,6 @@ export default defineComponent({
&:active { &:active {
background: var(--grey-100); background: var(--grey-100);
} }
}
}
}
}
} }
// HACK: // HACK: