1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { useTemplateRef, ref, onBeforeUnmount } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { onKeyStroke, onStartTyping, watchDebounced } from '@vueuse/core'
import { useMainStore } from '../store/main'
export const SearchInput = () => {
const
router = useRouter(),
route = useRoute(),
mainStore = useMainStore(),
searchQuery = ref(route.query.q),
searchInput = useTemplateRef<HTMLElement | null>('searchInput'),
cancelListenerEsc = onKeyStroke(
'Escape',
(event) => {
if (!searchInput.value) return
event.preventDefault();
if (searchInput.value !== document.activeElement) {
if (route.name !== 'search') return;
searchQuery.value = ''
router.back()
} else {
searchInput.value.blur()
}
}
),
cancelListenerCtrlK = onKeyStroke(
e => e.key === 'k' && e.ctrlKey,
(event) => {
if (!searchInput.value) return
event.preventDefault()
searchInput.value.focus()
}
)
onBeforeUnmount(() => {
cancelListenerEsc()
cancelListenerCtrlK()
})
onStartTyping(
() => {
if (!searchInput.value) return
if (searchInput.value !== document.activeElement) {
searchQuery.value = ''
searchInput.value.focus()
}
}
)
watchDebounced(
searchQuery,
() => {
const queryValue = searchQuery.value
if (queryValue !== '') {
router.push({
name: 'search',
replace: route.name === 'search',
query: { q: queryValue },
})
} else {
if (route.name !== 'search') return;
router.back()
}
},
{ debounce: 500, maxWait: 1000 },
)
return vine`
<div class="search">
<button :class="{ invisible: !$route.meta.backButton }" @click="router.back()">
<Icon icon="back" />
</button>
<input type="search" placeholder="Search" ref="searchInput" v-model.trim="searchQuery">
<button :class="{ invisible: !mainStore.isLoading }" aria-busy="true" />
</div>
`
}