57 lines
2.0 KiB
JavaScript
57 lines
2.0 KiB
JavaScript
import { ref } from 'vue'
|
|
import api from '../api'
|
|
|
|
/**
|
|
* "Did you mean" for a search that found nothing.
|
|
*
|
|
* ONE composable for every list in the product. Thirty routes take a search
|
|
* parameter across a dozen files, so carrying a suggestion in each of their
|
|
* responses would be a large change now and a thing every future plugin has to
|
|
* remember. Instead a page that renders no rows asks for a spelling, and a page
|
|
* that has not adopted this shows nothing - exactly what it showed before.
|
|
*
|
|
* Usage in a list view:
|
|
*
|
|
* const { suggestion, checkSuggestion, clearSuggestion } = useSearchSuggestion()
|
|
* // after a fetch:
|
|
* items.value.length ? clearSuggestion() : checkSuggestion(searchTerm.value)
|
|
*
|
|
* and in the template, beside the empty state:
|
|
*
|
|
* <SearchSuggestion :suggestion="suggestion" @pick="applySuggestion" />
|
|
*/
|
|
export function useSearchSuggestion() {
|
|
const suggestion = ref(null)
|
|
// Guards a slow answer arriving after a newer search has already been typed,
|
|
// which would offer a spelling for a query nobody is looking at any more.
|
|
let latest = 0
|
|
|
|
function clearSuggestion() {
|
|
suggestion.value = null
|
|
latest += 1
|
|
}
|
|
|
|
async function checkSuggestion(query) {
|
|
const term = (query || '').trim()
|
|
if (term.length < 2) {
|
|
clearSuggestion()
|
|
return
|
|
}
|
|
const ticket = ++latest
|
|
try {
|
|
const response = await api.get('/search/suggest', { params: { q: term } })
|
|
if (ticket !== latest) return
|
|
const offered = response.data?.data?.suggestion || null
|
|
// Never offer back what was typed: a suggestion identical to the query
|
|
// reads as a broken feature.
|
|
suggestion.value = offered && offered !== term.toLowerCase() ? offered : null
|
|
} catch (err) {
|
|
// A search that found nothing is already the answer. Failing to improve
|
|
// on it is not worth an error in front of anyone.
|
|
if (ticket === latest) suggestion.value = null
|
|
}
|
|
}
|
|
|
|
return { suggestion, checkSuggestion, clearSuggestion }
|
|
}
|