87 lines
2.8 KiB
Vue
87 lines
2.8 KiB
Vue
<script setup lang="ts">
|
|
import { notes, findNotes, setActiveNote } from '@/composables/useNotes'
|
|
import SearchResult from '@/components/Search/SearchResult.vue'
|
|
|
|
const emit = defineEmits<{
|
|
active: [active: boolean]
|
|
}>()
|
|
|
|
const active = ref<boolean>(false)
|
|
watch(active, () => {
|
|
if (!active.value) {
|
|
query.value = ''
|
|
activeResult.value = undefined
|
|
}
|
|
emit('active', active.value)
|
|
})
|
|
|
|
const query = ref<string>('')
|
|
const results = computed<Note[]>(() => {
|
|
return query.value ? findNotes(query.value) : notes.value
|
|
})
|
|
|
|
const goToNote = (note: Note) => {
|
|
setActiveNote(note.id)
|
|
active.value = false
|
|
if (queryElem.value) queryElem.value.blur()
|
|
}
|
|
const queryElem = ref<HTMLInputElement | null>(null)
|
|
|
|
const activeResult = ref<Note>()
|
|
|
|
const handleKeydown = (event: KeyboardEvent) => {
|
|
const code = event.code
|
|
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code)) {
|
|
let index = results.value.findIndex((note) => note.id === activeResult.value?.id)
|
|
if (['ArrowDown', 'Tab'].includes(code)) {
|
|
index++
|
|
} else if (['ArrowUp'].includes(code)) {
|
|
index--
|
|
}
|
|
if (index + 1 > results.value.length) index = index - results.value.length
|
|
if (index < 0) index = results.value.length - 1
|
|
activeResult.value = results.value[index]
|
|
const element = resultsRefs.value[index].$el
|
|
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code)) element.scrollIntoView({ block: 'nearest' })
|
|
} else if (code === 'Enter' && activeResult.value) {
|
|
goToNote(activeResult.value)
|
|
} else if (code === 'Escape' && queryElem.value) {
|
|
queryElem.value.blur()
|
|
}
|
|
}
|
|
|
|
const resultsRefs = ref<InstanceType<typeof SearchResult>[]>([])
|
|
</script>
|
|
<template>
|
|
<div id="search-container" class="relative h-full flex-grow">
|
|
<input
|
|
type="text"
|
|
placeholder="Search for notes"
|
|
class="h-full w-full rounded border-0 bg-white/10 px-2 text-white outline-none placeholder:text-white focus:bg-white focus:text-black"
|
|
@focus="active = true"
|
|
@mousedown="active = true"
|
|
@blur="active = false"
|
|
v-model="query"
|
|
ref="queryElem"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
<div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active">
|
|
<UIMenu :compact="true" class="mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow">
|
|
<div class="max-h-[320px] w-full overflow-y-auto">
|
|
<template v-if="results.length > 0">
|
|
<SearchResult
|
|
v-for="result in results"
|
|
:key="result.id"
|
|
:result="result"
|
|
:active-result="activeResult"
|
|
@go-to-note="goToNote(result)"
|
|
ref="resultsRefs"
|
|
/>
|
|
</template>
|
|
<UIMenuItem :compact="true" v-else>No notes found</UIMenuItem>
|
|
</div>
|
|
</UIMenu>
|
|
</div>
|
|
</div>
|
|
</template>
|