funkwhale/front/src/composables/useSmartSearch.ts

86 lines
2.1 KiB
TypeScript

import type { MaybeRef } from '@vueuse/core'
import type { Token } from '~/utils/search'
import { refWithControl } from '@vueuse/core'
import { computed, ref, unref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { compileTokens, normalizeQuery, parseTokens } from '~/utils/search'
export interface SmartSearchProps {
defaultQuery?: string
updateUrl?: boolean
}
export default (defaultQuery: MaybeRef<string>, updateUrl: MaybeRef<boolean>) => {
const query = refWithControl(unref(defaultQuery))
const tokens = ref([] as Token[])
watch(query, (value) => {
tokens.value = parseTokens(normalizeQuery(value))
}, { immediate: true })
const updateHandlers = new Set<() => void>()
const onSearch = (fn: () => void) => {
updateHandlers.add(fn)
return () => updateHandlers.delete(fn)
}
const router = useRouter()
watch(tokens, (value) => {
const newQuery = compileTokens(value)
if (unref(updateUrl)) {
return router.replace({ query: { q: newQuery } })
}
// TODO (wvffle): updateUrl = false only in FilesTable.vue
query.set(newQuery, false)
for (const handler of updateHandlers) {
handler()
}
// this.page = 1
// this.fetchData()
}, { deep: true })
const getTokenValue = (key: string, fallback: string) => {
const matching = tokens.value.find(token => {
return token.field === key
})
return matching?.value ?? fallback
}
const addSearchToken = (key: string, value: string) => {
if (value === '') {
tokens.value = tokens.value.filter(token => {
return token.field !== key
})
return
}
const existing = tokens.value.filter(token => {
return token.field === key
})
if (!existing.length) {
tokens.value.push({ field: key, value })
return
}
// TODO (wvffle): Check if triggers reactivity
for (const token of existing) {
token.value = value
}
}
return {
getTokenValue,
addSearchToken,
onSearch,
query: computed({
get: () => compileTokens(tokens.value),
set: (value: string) => query.set(value, true)
})
}
}