diff --git a/client/src/components/AppHeader.vue b/client/src/components/AppHeader.vue index 964803daf..7516fffac 100644 --- a/client/src/components/AppHeader.vue +++ b/client/src/components/AppHeader.vue @@ -1,153 +1,361 @@ - - - - - - - {{ title }} - - {{ loaded.toLocaleString() }}/{{ total.toLocaleString() }} - - - - - - - + + + + + + - + + + + + Searching... + No results + + + + {{ result.title ?? 'Untitled' }} + {{ result.authors.join(', ') }} + + + {{ result.libraryName }} + + + + + + + + + + + + + + {{ title }} + + + {{ loaded.toLocaleString() }}/{{ total.toLocaleString() }} + - + + + + + + + - - - - - + + + Searching... + No results + + + + {{ result.title ?? 'Untitled' }} + {{ result.authors.join(', ') }} + + + {{ result.libraryName }} + + + + + + + + + + + + + + + + - - - - Display + + + + - - - - Cover size - {{ props.coverSize }}px + + + + + + + + + + + + Display + + + Cover size + {{ props.coverSize }}px + + - - - - - - - Grid gap - {{ props.gridGap }}px + + + Grid gap + {{ props.gridGap }}px + + + + + + Accent + + + + Radius + - + + - + - - - Accent - - + + + + - - - Radius - - - - - - - - - - - - - - - - - - + + + + + + + + + View + + Grid + List + + + + + Display + + + + + {{ themeStore.theme === 'dark' ? 'Light mode' : 'Dark mode' }} + + + + + + + + + + + Display + + + + + Cover size + {{ props.coverSize }}px + + + + + + Grid gap + {{ props.gridGap }}px + + + + + + Accent + + + + Radius + + + + + diff --git a/client/src/features/book/composables/useGlobalSearch.ts b/client/src/features/book/composables/useGlobalSearch.ts new file mode 100644 index 000000000..ab7477460 --- /dev/null +++ b/client/src/features/book/composables/useGlobalSearch.ts @@ -0,0 +1,46 @@ +import { ref, watch, type Ref } from 'vue' +import { api } from '@/lib/api' + +export interface GlobalSearchResult { + id: number + title: string | null + authors: string[] + libraryId: number + libraryName: string +} + +export function useGlobalSearch(query: Ref) { + const results = ref([]) + const loading = ref(false) + let timer: ReturnType | null = null + + watch(query, (q) => { + if (timer) clearTimeout(timer) + if (q.trim().length < 2) { + results.value = [] + loading.value = false + return + } + loading.value = true + timer = setTimeout(async () => { + try { + const params = new URLSearchParams({ q: q.trim(), limit: '10' }) + const res = await api(`/api/books/search?${params}`) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + results.value = await res.json() + } catch { + results.value = [] + } finally { + loading.value = false + } + }, 300) + }) + + function clear() { + if (timer) clearTimeout(timer) + results.value = [] + loading.value = false + } + + return { results, loading, clear } +} diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue index dad9739c7..9a47d431a 100644 --- a/client/src/views/HomeView.vue +++ b/client/src/views/HomeView.vue @@ -1,6 +1,7 @@
{{ result.title ?? 'Untitled' }}
{{ result.authors.join(', ') }}
Display