feat: add currently_reading and read and repsonsive menu
This commit is contained in:
@@ -1,31 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
useHead({ title: 'Wunschzettel' })
|
||||
useHead({ title: 'Wunschzettel', htmlAttrs: { lang: 'de' } })
|
||||
|
||||
const search = ref('')
|
||||
const sort = ref<WishlistSort>('title')
|
||||
const sortItems = [
|
||||
{ label: 'Author', value: 'author' },
|
||||
{ label: 'Titel', value: 'title' }
|
||||
]
|
||||
|
||||
const { books, total, error, status } = useWishlist(search, sort)
|
||||
const { books, total, readingBooks, readingTotal, readBooks, readTotal, error, status } = useWishlist(search, sort)
|
||||
|
||||
// On desktop the controls sit in the header; the floating bar only drops in at the top once they have scrolled out of view.
|
||||
// On mobile the header controls are hidden, so the bar is always there.
|
||||
const headerControls = useTemplateRef<HTMLElement>('headerControls')
|
||||
const headerControlsVisible = ref(true)
|
||||
|
||||
let observer: IntersectionObserver | undefined
|
||||
|
||||
onMounted(() => {
|
||||
observer = new IntersectionObserver(([entry]) => {
|
||||
headerControlsVisible.value = !!entry?.isIntersecting
|
||||
})
|
||||
if (headerControls.value) observer.observe(headerControls.value)
|
||||
})
|
||||
onBeforeUnmount(() => observer?.disconnect())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMain>
|
||||
<UContainer class="py-8">
|
||||
<div class="mb-8 flex flex-wrap items-center justify-between gap-3">
|
||||
<h1 class="text-2xl font-semibold text-highlighted">
|
||||
Huaqo's Wunschzettel
|
||||
</h1>
|
||||
<div class="flex w-full items-center gap-2 sm:w-auto">
|
||||
<UInput
|
||||
v-model="search"
|
||||
icon="i-lucide-search"
|
||||
placeholder="Title, author, ISBN or link"
|
||||
class="flex-1 sm:w-72 sm:flex-none"
|
||||
/>
|
||||
<USelect v-model="sort" :items="sortItems" icon="i-lucide-arrow-down-a-z" class="w-32" />
|
||||
<UContainer class="pt-8 pb-32 sm:pb-8">
|
||||
<div class="mb-8 flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold text-highlighted">
|
||||
Huaqo's Wunschzettel
|
||||
<span v-if="total" class="text-base font-normal text-muted tabular-nums">{{ books.length === total ? total : `${books.length} / ${total}` }}</span>
|
||||
</h1>
|
||||
<p class="mt-1 text-sm text-muted">
|
||||
Überrasch mich gern auch mit etwas, das hier nicht zu finden ist <3
|
||||
</p>
|
||||
</div>
|
||||
<div ref="headerControls" class="hidden w-96 sm:block">
|
||||
<WishlistControls v-model:search="search" v-model:sort="sort" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +45,7 @@ const { books, total, error, status } = useWishlist(search, sort)
|
||||
color="error"
|
||||
variant="subtle"
|
||||
icon="i-lucide-triangle-alert"
|
||||
title="Couldn't load the wishlist"
|
||||
title="Der Wunschzettel konnte nicht geladen werden"
|
||||
:description="(error.data as { statusMessage?: string } | undefined)?.statusMessage || error.statusMessage || error.message"
|
||||
class="mb-6"
|
||||
/>
|
||||
@@ -43,9 +54,24 @@ const { books, total, error, status } = useWishlist(search, sort)
|
||||
<WishlistCard v-for="book in books" :key="book.id" :book="book" />
|
||||
</div>
|
||||
|
||||
<p v-if="!books.length && !error && status !== 'pending'" class="py-16 text-center text-muted">
|
||||
{{ search ? 'No book matches your search.' : 'The wishlist is empty.' }}
|
||||
<p v-if="!books.length && !readingBooks.length && !readBooks.length && !error && status !== 'pending'" class="py-16 text-center text-muted">
|
||||
{{ search ? 'Kein Buch passt zu deiner Suche.' : 'Der Wunschzettel ist leer.' }}
|
||||
</p>
|
||||
|
||||
<WishlistShelf v-if="readingBooks.length" title="Gerade am Lesen" :books="readingBooks" :total="readingTotal" />
|
||||
<WishlistShelf v-if="readBooks.length" title="Bereits gelesen" :books="readBooks" :total="readTotal" />
|
||||
</UContainer>
|
||||
|
||||
<!-- Keeps search and sort reachable while scrolling: always at the bottom on mobile, at the top on desktop -->
|
||||
<div class="pointer-events-none fixed inset-x-0 bottom-0 z-20 flex justify-center px-4 pb-[max(1rem,env(safe-area-inset-bottom))] sm:top-0 sm:bottom-auto sm:pt-4 sm:pb-0">
|
||||
<WishlistControls
|
||||
v-model:search="search"
|
||||
v-model:sort="sort"
|
||||
floating
|
||||
:inert="headerControlsVisible || undefined"
|
||||
class="pointer-events-auto w-full max-w-md rounded-full bg-default/80 p-2 shadow-lg ring-1 ring-default backdrop-blur transition duration-200"
|
||||
:class="{ 'sm:pointer-events-none sm:-translate-y-[150%] sm:opacity-0': headerControlsVisible }"
|
||||
/>
|
||||
</div>
|
||||
</UMain>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user