All files / src/composables useDebouncedSearch.ts

95.65% Statements 22/23
100% Branches 10/10
85.71% Functions 6/7
95.65% Lines 22/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                      127x 127x 127x 127x     5x 1x 1x         85x 85x 85x       5x 3x     5x   5x 5x 5x 5x       127x 85x 3x       127x       127x              
import { onBeforeUnmount, ref, watch } from 'vue'
 
interface DebouncedSearchOptions {
  auto?: boolean
}
 
export function useDebouncedSearch(
  onDebouncedSearch: (query: string) => Promise<void> | void,
  delay = 300,
  options: DebouncedSearchOptions = {}
) {
  const searchQuery = ref('')
  const appliedQuery = ref('')
  let searchTimeout: ReturnType<typeof setTimeout> | null = null
  const auto = options.auto ?? true
 
  function clearPendingSearch() {
    if (searchTimeout) {
      clearTimeout(searchTimeout)
      searchTimeout = null
    }
  }
 
  async function runSearchNow() {
    const normalizedQuery = searchQuery.value.trim()
    appliedQuery.value = normalizedQuery
    await onDebouncedSearch(normalizedQuery)
  }
 
  function scheduleSearch(query?: string) {
    if (typeof query === 'string') {
      searchQuery.value = query
    }
 
    clearPendingSearch()
 
    searchTimeout = setTimeout(() => {
      const normalizedQuery = searchQuery.value.trim()
      appliedQuery.value = normalizedQuery
      void onDebouncedSearch(normalizedQuery)
    }, delay)
  }
 
  if (auto) {
    watch(searchQuery, (value) => {
      scheduleSearch(value)
    })
  }
 
  onBeforeUnmount(() => {
    clearPendingSearch()
  })
 
  return {
    searchQuery,
    appliedQuery,
    runSearchNow,
    scheduleSearch
  }
}