zaphyra's git: domsonic

subsonic web-client

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>
  `
}